How do I conditionally update List<Object> items using LINQ - c#

How do I conditionally update List<object> items using LINQ, I have managed to get a way to update all items in List<object> using this syntax:
var updatedList = sourceList.Select( x => { x.PropertyA = NewValue; return x; }).ToList();

var updatedList = sourceList.Select( x => {return x = x.PropertyA == SomeValue ? x1 : x;}).ToList();

You can either project it to a new collection using the .Select, or affect the original list using the .ForEach extension method. Both are shown below, given an arbitrary condition.
class Program
{
static void Main(string[] args)
{
List<testClass> ogList = new List<testClass>();
bool testCondition = true;
//use the select to update and project to new collection
var updated =
ogList
.Select(x =>
{
if (testCondition)
x.testProp = 1;
return x;
}).ToList();
//use the foreach to update original collection
ogList.ForEach(x =>
{
if (testCondition)
x.testProp = 1;
});
}
}
public class testClass
{
public int testProp { get; set; }
}

You can use this code:
sourceList.Where(w => w.CheckProperty == valueofProperty).Select(s => {
s.PropertyA = valueofPropertyA ;
s.PropertyB = valueofPropertyB;
return s;
}).ToList();

Hope this answer will help you.
So here I creating objects of my database, list of my db_table and table.
private DB dbs = new DB();
List<tab1> lst = new List<tab1>();
tab1 tabobj = new tab1();
here you will get the list of data in your table
lst = dbs.tab1.ToList();
````````````
updating the table
```````````
tabobj.c1 = "value";
tabobj.c2 = "value";
adding updated columns to table
dbs.tab1.Add(tabobj);
save changes here
dbs.SaveChanges();
data of updated table
lst = dbs.tab1.ToList();

Related

How to define local variable in a select LINQ-query

I try to write this code:
returnModel.BunkerStat=tmpBunk.Select(b =>
new BunkerStatisticsRowModel
{
numberOfBunk=b.NumberOfBunk,
procentOfBunk=numberOfBunk/100
}).ToList();
But I get compile error because I cant reuse numberOfBunk to calculate procentOfBunk. Anybody know How I can reuse numberOfBunk? I dont want to repeat b.NumberOfBunk. Thanks.
SOLVED:
I tried like this and it worked:
returnModel.BunkerStat=tmpBunk.Select(b =>
new BunkerStatisticsRowModel
{
numberOfBunk=b.NumberOfBunk,
procentOfBunk=numberOfBunk/100
}).Select((p)=>
new BunkerStatisticsRowModel()
{
numberOfBunk=p.numberOfBunk,
procentOfBunk=p.numberOfBunk/100
}).ToList();
Use lambda with body
returnModel.BunkerStat = tmpBunk.Select(b => {
var procentOfBunk = b.NumberOfBunk / 100;
return new BunkerStatisticsRowModel
{
numberOfBunk = b.NumberOfBunk,
procentOfBunk = procentOfBunk
};
})
.ToList();
If you are able to add Properties in your Model BunkerStatisticsRowModel.
public class BunkerStatisticsRowModel
{
public int procentOfBunk { get { return numberOfBunk / 100; } }
public int numberOfBunk { get; set; }
}
Then use your linq statement like this.
returnModel.BunkerStat=tmpBunk.Select(b =>
new BunkerStatisticsRowModel
{
numberOfBunk=b.NumberOfBunk
}).ToList();
you should have your Collection which contains numberOfBunk and procentOfBunk.
You can use statement lambda to create a temporary variable.
returnModel.BunkerStat = tmpBunk
.Select(b =>
{
var procentOfBunk = b.NumberOfBunk / 100;
return new BunkerStatisticsRowModel
{
numberOfBunk = b.NumberOfBunk,
procentOfBunk = procentOfBunk,
};
})
.ToList();

How to remove duplicates from object list based on that object property in c#

I've got a problem with removing duplicates at runtime from my list of object.
I would like to remove duplicates from my list of object and then set counter=counter+1 of base object.
public class MyObject
{
MyObject(string name)
{
this.counter = 0;
this.name = name;
}
public string name;
public int counter;
}
List<MyObject> objects_list = new List<MyObject>();
objects_list.Add(new MyObject("john"));
objects_list.Add(new MyObject("anna"));
objects_list.Add(new MyObject("john"));
foreach (MyObject my_object in objects_list)
{
foreach (MyObject my_second_object in objects_list)
{
if (my_object.name == my_second_object.name)
{
my_object.counter = my_object.counter + 1;
objects_list.remove(my_second_object);
}
}
}
It return an error, because objects_list is modified at runtime. How can I get this working?
With a help of Linq GroupBy we can combine duplicates in a single group and process it (i.e. return an item which represents all the duplicates):
List<MyObject> objects_list = ...
objects_list = objects_list
.GroupBy(item => item.name)
.Select(group => { // given a group of duplicates we
var item = group.First(); // - take the 1st item
item.counter = group.Sum(g => g.counter); // - update its counter
return item; // - and return it instead of group
})
.ToList();
The other answer seem to be correct, though I think it will do scan of the whole list twice, depending on your requirement this might or might not be good enough. Here is how you can do it in one go:
var dictionary = new Dictionary<string, MyObject>();
foreach(var obj in objects_list)
{
if(!dictionary.ContainsKey(obj.name)
{
dictionary[obj.name] = obj;
obj.counter++;
}
else
{
dictionary[obj.name].counter++;
}
}
Then dictionary.Values will contain your collection

Select random record from table with further conditions in C# code

I'm coding little process where I can select random record from table, where field with checkbox/boolean is also unchecked.
The moral of the story is to select random record, work with it and set checkbox/boolean field == Checked.
I have approach this work by selecting random record from Count() method, I got Integer in return and I can choose random number from that pool. But I also need to filter the query to select only records who have the checkbox/boolean field unchecked, but I haven't managed in the following code to go so far.
I have used Lambda expression as follows but maybe better way can exists.
public class Program
{
static void Main(string[] args)
{
DatabaseModelContainer mc = new DatabaseModelContainer();
MyTable myTable = new MyTable();
Random rnd = new Random();
int index = rnd.Next(myTable.Count);
string selectedRec = myTable.MyField.OrderBy(r => myTable.MyField).Take(index).ToString();
//Selected record to be shown at the screen, but will not appear (only reference point is shown)
Console.WriteLine(selectedRec);
//Selected random value is displayed here and that indeed worked, I get random value there, but I can't use the id to select record above based on that id.
Console.WriteLine(index.ToString());
Console.ReadLine();
}
}
Code above does NOT show who to choose random record WHERE CheckboxField == Checked, so that code needs to be added as well.
What approach is best?
Assuming your MyTable is a sequence, the correct way of doing it should be :
var randomRecord = myTable
.Where(x => x.CheckboxField)
.ToList()
.OrderBy(x => rnd.Next())
.FirstOrDefault();
Full Example:
See it here: https://dotnetfiddle.net/B4JLU0
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
class TableRow
{
public int I {get;set;}
public bool CheckboxField {get;set;}
TableRow(int i)
{
this.I = i;
this.CheckboxField = (i % 3 == 0); //in our demo, assume that some are already checked, some aren't
}
public static IEnumerable<TableRow> GenerateTableRows(int howMany)
{
for (int i=0; i<howMany; i++)
{
yield return new TableRow(i);
}
}
}
public static void Main()
{
new Program();
Console.WriteLine("Done");
}
Program()
{
ICollection<TableRow> myTable = new List<TableRow>(TableRow.GenerateTableRows(12));
while (myTable.Count(x => !x.CheckboxField) > 0) //keep looping until we've processed all records
{
var randomRecord = GetRandomRecord(myTable);
Console.WriteLine(randomRecord == null ? "No Matching Rows Exist" : randomRecord.I.ToString());
}
}
TableRow GetRandomRecord(ICollection<TableRow> myTable)
{
var rnd = new Random(); //we could optionally provide this as a parameter too to avoid recreating each time
var randomRecord = myTable
.Where(x => !x.CheckboxField)
.ToList()
.OrderBy(x => rnd.Next())
.FirstOrDefault();
if(randomRecord != null) //avoid issue if all items are checked
{
randomRecord.CheckboxField = true; //mark this record as having been processed
}
return randomRecord;
}
}
Should be able to do this:
var randomNumberGenerator = new Random();
var randomNumber = randomNumberGenerator.Next(myTable.Count(r => r.CheckboxField)) - 1;
myTable
.Where(r => r.CheckboxField)
.Skip(randomNumber)
.Select(r => r.DesiredColumn)
.FirstOrDefault();

Multiple OfType Linq?

I have a linq query that selects all textboxes in a placeholder and adds them to a list using a struct. I need to expand this functionality to also take the selectedvalue of a DropDownList I am pretty sure I am doing this wrong, because when I debug the method the lists count is 0.
My own guess is that declaring 2 OfType<>() is wrong, but I am pretty new to linq and I have no idea of how else to do it.
Any help would be awesome! Thanks in advance.
Here's what I have so far:
public struct content
{
public string name;
public string memberNo;
public int points;
public string carclass;
}
List<content> rows = new List<content>();
protected void LinkButton_Submit_Attendees_Click(object sender, EventArgs e)
{
List<content> rows = PlaceHolder_ForEntries.Controls.OfType<TextBox>().OfType<DropDownList>()
.Select(txt => new
{
Txt = txt,
Number = new String(txt.ID.SkipWhile(c => !Char.IsDigit(c)).ToArray())
})
.GroupBy(x => x.Number)
.Select(g => new content
{
carclass = g.First(x => x.Txt.ID.StartsWith("DropDownlist_CarClass")).Txt.SelectedValue,
name = g.First(x => x.Txt.ID.StartsWith("TextBox_Name")).Txt.Text,
memberNo = g.First(x => x.Txt.ID.StartsWith("TextBox_MemberNo")).Txt.Text,
points = int.Parse(g.First(x => x.Txt.ID.StartsWith("TextBox_Points")).Txt.Text)
})
.ToList();
}
Here's the method that creates the controls.
protected void createcontrols()
{
int count = 0;
if (ViewState["count"] != null)
{
count = (int)ViewState["count"];
}
while (PlaceHolder_ForEntries.Controls.Count < count)
{
TextBox TextBox_Name = new TextBox();
TextBox TextBox_MemberNo = new TextBox();
TextBox TextBox_Points = new TextBox();
DropDownList DropDownList_CarClass = new DropDownList();
DropDownList_CarClass.Items.Add("Car1");
...
DropDownList_CarClass.Items.Add("Car2");
TextBox_Name.Attributes.Add("placeholder", "Navn");
TextBox_Name.ID = "TextBox_Name" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_Name.CssClass = "input-small";
TextBox_MemberNo.Attributes.Add("placeholder", "Medlemsnr.");
TextBox_MemberNo.ID = "TextBox_MemberNo" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_MemberNo.CssClass = "input-small";
TextBox_Points.Attributes.Add("placeholder", "Point");
TextBox_Points.ID = "TextBox_Points" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_Points.CssClass = "input-small";
PlaceHolder_ForEntries.Controls.Add(TextBox_Name);
PlaceHolder_ForEntries.Controls.Add(TextBox_MemberNo);
PlaceHolder_ForEntries.Controls.Add(DropDownList_CarClass);
PlaceHolder_ForEntries.Controls.Add(TextBox_Points);
PlaceHolder_ForEntries.Controls.Add(new LiteralControl("<br />"));
}
}
you can use the Where and check if the instance of object is of type!
List<content> rows = PlaceHolder_ForEntries.Controls.Cast<Control>().Where(c => c is TextBox || c is DropDownList)
.Select(txt => new
{
Txt = txt,
Number = new String(txt.ID.SkipWhile(c => !Char.IsDigit(c)).ToArray())
})
.GroupBy(x => x.Number)
.Select(g => new content
{
carclass = g.First(x => x.Txt.ID.StartsWith("DropDownlist_CarClass")).Txt.SelectedValue,
name = g.First(x => x.Txt.ID.StartsWith("TextBox_Name")).Txt.Text,
memberNo = g.First(x => x.Txt.ID.StartsWith("TextBox_MemberNo")).Txt.Text,
points = int.Parse(g.First(x => x.Txt.ID.StartsWith("TextBox_Points")).Txt.Text)
})
.ToList();
AppDeveloper is right. OfType<T> filters out all objects of types other than T; so by filtering twice, you effectively eliminate all objects in the list.
If you wanted to wrap this logic (filtering all but two types from a list) into something reusable, nothing's stopping you from implementing your own extension method:
using System.Collections;
public static class EnumerableExtensions
{
public static IEnumerable OfType<T1, T2>(this IEnumerable source)
{
foreach (object item in source)
{
if (item is T1 || item is T2)
{
yield return item;
}
}
}
}
Including the above class in your project will allow you to write code like this in your application:
var textBoxesAndDropDowns = controls.OfType<TextBox, DropDownList>();
To learn more about extension methods, see the MSDN article on the subject.
Note that since the extension method above "lets in" two different types, the result is still a non-generic IEnumerable sequence. If you wanted to treat the result as a generic sequence (e.g., an IEnumerable<Control>), I would recommend using the Cast<T> extension method:
var filteredControls = controls.OfType<TextBox, DropDownList>().Cast<Control>();
I haven't read the question thoroughly, but from what the title implies, you can achieve the behavior with:
var collection = new object[] { 5, "4545", 'd', 54.5 , 576 };
var allowedTypes = new[] { typeof(string), typeof(int) };
var result = collection
.Where(item => allowedTypes.Contains(item.GetType()));
See it in action here.
Took #Shimmys answer for an extension method:
/// <param name="wantedTypes">--- Sample: --- new Type[] { typeof(Label), typeof(Button) }</param>
public static IEnumerable OfTypes(this IEnumerable collection, Type[] wantedTypes)
{
if (wantedTypes == null)
return null;
else
return collection.Cast<object>().Where(element => wantedTypes.Contains(element.GetType()));
}
Usage:
// List of 3 different controls
List<object> controls = new List<object>(new object[] { new Label(), new Button(), new TextBox() });
// Get all labels and buttons
var labelsAndButtons = controls.OfTypes(new Type[] { typeof(Label), typeof(Button) });
Your problem is in the OfType<>().OfType<>() you filter twice with different types

Elegant way to check if a list contains an object where one property is the same, and replace only if the date of another property is later

I have a class as follows :
Object1{
int id;
DateTime time;
}
I have a list of Object1. I want to cycle through another list of Object1, search for an Object1 with the same ID and replace it in the first list if the time value is later than the time value in the list. If the item is not in the first list, then add it.
I'm sure there is an elegant way to do this, perhaps using linq? :
List<Object1> listOfNewestItems = new List<Object1>();
List<Object1> listToCycleThrough = MethodToReturnList();
foreach(Object1 object in listToCycleThrough){
if(listOfNewestItems.Contains(//object1 with same id as object))
{
//check date, replace if time property is > existing time property
} else {
listOfNewestItems.Add(object)
}
Obviously this is very messy (and that's without even doing the check of properties which is messier again...), is there a cleaner way to do this?
var finalList = list1.Concat(list2)
.GroupBy(x => x.id)
.Select(x => x.OrderByDescending(y=>y.time).First())
.ToList();
here is the full code to test
public class Object1
{
public int id;
public DateTime time;
}
List<Object1> list1 = new List<Object1>()
{
new Object1(){id=1,time=new DateTime(1991,1,1)},
new Object1(){id=2,time=new DateTime(1992,1,1)}
};
List<Object1> list2 = new List<Object1>()
{
new Object1(){id=1,time=new DateTime(2001,1,1)},
new Object1(){id=3,time=new DateTime(1993,1,1)}
};
and OUTPUT:
1 01.01.2001
2 01.01.1992
3 01.01.1993
This is how to check:
foreach(var object in listToCycleThrough)
{
var currentObject = listOfNewestItems
.SingleOrDefault(obj => obj.Id == object.Id);
if(currentObject != null)
{
if (currentObject.Time < object.Time)
currentObject.Time = object.Time
}
else
listOfNewestItems.Add(object)
}
But if you have large data, would be suggested to use Dictionary in newest list, time to look up will be O(1) instead of O(n)
You can use LINQ. Enumerable.Except to get the set difference(the newest), and join to find the newer objects.
var listOfNewestIDs = listOfNewestItems.Select(o => o.id);
var listToCycleIDs = listToCycleThrough.Select(o => o.id);
var newestIDs = listOfNewestIDs.Except(listToCycleIDs);
var newestObjects = from obj in listOfNewestItems
join objID in newestIDs on obj.id equals objID
select obj;
var updateObjects = from newObj in listOfNewestItems
join oldObj in listToCycleThrough on newObj.id equals oldObj.id
where newObj.time > oldObj.time
select new { oldObj, newObj };
foreach (var updObject in updateObjects)
updObject.oldObj.time = updObject.newObj.time;
listToCycleThrough.AddRange(newestObjects);
Note that you need to add using System.Linq;.
Here's a demo: http://ideone.com/2ASli
I'd create a Dictionary to lookup the index for an Id and use that
var newItems = new List<Object1> { ...
IList<Object1> itemsToUpdate = ...
var lookup = itemsToUpdate.
Select((i, o) => new { Key = o.id, Value = i }).
ToDictionary(i => i.Key, i => i.Value);
foreach (var newItem in newitems)
{
if (lookup.ContainsKey(newitem.ID))
{
var i = lookup[newItem.Id];
if (newItem.time > itemsToUpdate[i].time)
{
itemsToUpdate[i] = newItem;
}
}
else
{
itemsToUpdate.Add(newItem)
}
}
That way, you wouldn't need to reenumerate the list for each new item, you'd benefit for the hash lookup performance.
This should work however many times an Id is repeated in the list of new items.

Categories