Remove an item from a list - c#

why cant I select remove or delete? I want to remove a record from a list
IEnumerable<StockLocation_Table> AllCurrentStocklocations = db.StockLocation_Table.ToList();
List<StockLocation> StockLocations = ServerHelper.GetStockLocationsBatch(BatchUrl, i, batchSize);
foreach (StockLocation_Table _stock_table in AllCurrentStocklocations)
{
foreach (StockLocation _stock in StockLocations)
{
if (_stock.ServerLocationId == _stock_table.ServerLocationId)
{
AllCurrentStocklocations.?? why cant i say remove._stock_table
}
}
}

Because it is IEnumerable<T> and the Remove method is not defined in IEnumerable<T>.Since you are using ToList just use a List<T> as type:
List<StockLocation_Table> AllCurrentStocklocations = db.StockLocation_Table.ToList();
Edit: Also you can't modify the collection inside of foreach loop.You can use LINQ instead:
var AllCurrentStocklocations = db.StockLocation_Table
.Where(x => !StockLocations
.Any(s => s.ServerLocationId == x.ServerLocationId).ToList()

What you want to do here is get all of the items from your DB table where the ID is not in this other list. What you should do here is construct a query such that you get just those items without those IDs, rather than pulling down the entire DB table into a list, and then going through this other list for each item) to look for IDs so that you can remove the current item from this list. In addition to being super inefficient, this would also mean removing the item from a collection being iterated, which would break the iterator. Instead write something that can be translated into a DB query:
List<StockLocation> stockLocations = ServerHelper.GetStockLocationsBatch(
BatchUrl, i, batchSize);
var locationIDs = stockLocations.Select(location => location.ServerLocationId);
db.StockLocation_Table.Where(item =>
!locationIDs.Contains(item.ServerLocationId));

Related

How to update multiple items in list

I have a list and a dictionary.
Dictionary's key and value are items of the list.
I want to loop through dictionary and if list.id and key match change item with value.
I did this and dont know if its right or wrong but worked for me:
foreach (KeyValuePair<string, string> entry in dict)
{
list.Where(x => x.Id== entry.Key)
.Select(y => { y.item= entry.Value; return y; })
.ToList();
}
Now also I need to change date with datetime.now. Can I do it in the same linq if I can how?
First, let's see what your current code does:
foreach (KeyValuePair<string, string> entry in dict) // loop through each key/value pair in the dictionary
{
list.Where(x => x.Id== entry.Key).Select(y => { y.item= entry.Value; return y; }) // construct a query on `list` and project the result using Select
.ToList(); // Force evaluation of the query in order to execute the projection
}
The projection code actually modifies the source item (y.item = ~~), which is why this "works", but it's definitely not the right way to do it.
Unless you have a huge list (in which case the list should probably be changed), you're doing this in the least efficient way possible. Dictionaries are designed for close to O(1) lookups, and you're throwing this away with the way that you're using it. You should instead iterate through the list:
foreach (var listItem in list)
And then see if there is a corresponding item in the dictionary:
if (dict.TryGetValue(listItem.Key, out var value))
From here, we can update the list item with the new value. Putting it all together:
foreach (var listItem in list)
{
if (dict.TryGetValue(listItem.Key, out var value))
{
listItem.item = value;
}
}
Generally, we should avoid Select for updating items in the list. For update, we have other LINQ extensions such as ForEach. Moreover, you can simplify the iterations.
list.Where(listItem => dict.Keys.Contains(listItem.Id))
.ToList()
.ForEach(listItem =>
{
listItem.item = dict[item.Id].Value;
});
If you have some aversion to using for loops you could use List.ForEach, which is a list thing not a LINQ thing:
list.ForEach(x => {
if(dictionary.TryGetValue(x.Id, out var s)) {
x.Item = s;
x.ModifiedDate = DateTime.Now;
}
});
But how about making the property setter for Item maintain the modified date then you don’t have to remember to set it everywhere you make a change to Item? (Though that is not an excuse for carrying on with the LINQ side effect route in the question)

Store single result as List<> from loop

Please check Part1 carefully. The variable "singleItem" containing the item during loop of each Ids. But my goal is convert this "singleItem" variable as a List of items. So i can use this "singleItem" in another method like Part2
Part1:
foreach (int Id in Ids)
{
var singleItem = ctx.SingleScannedItems.FirstOrDefault(x => x.SingleScannedItemId == Id);
}
Part2:
public string MyMethod(List<singleItem> items)
{
//do something with items
}
You can use Contains method to query all the items which has SingleScannedItemId property value matching to the values in the Ids collection.
var filteredItems = ctx.SingleScannedItems
.Where(x => Ids.Contains(x.SingleScannedItemId)).ToList();
You do not need the loop now as you are querying all those items you wanted once.
The variable filteredItems will be a list of SingleScannedItem. You can pass that to a method which accepts the collection.
You dont need loop , checking against a Ids list the following would work.
List<singleItem> items = ctx.SingleScannedItems
.Where(x => Ids.Contains(x.SingleScannedItemId).ToList();
MyMethod(items);

Select inside a select query (lists)

I have this code:
HashSet<string> MyHash = new HashSet<String>();
foreach (MyType a in myCollection)
{
foreach (string b in a.mylist)
{
MyHash.Add(b);
}
}
I tried to make it easier to read like this but it doesn't work:
myCollection.MyType.select(x => x.mylist.select(y => MyHash.add(y)));
Any suggestions?
Select will return a collection of items instead of modifying it. Thus you have to assign its return-value into a variable or member or pass it into a method. Furthermore you´d need to flatten the results to add the members of your inner list into the hashset.
Thus when you want to add the result into your list use HashSet.UnionWith:
myHash.UnionWith(myCollection.SelectMany(x => x.MyList));
Alternativly you can also use the constructor of HashSet that accepts a collection of items:
var myHash = new HashSet<string>(...);
However IMHO this isn´t any more readable than using some foreach-based approach.
Here's how I would do it, separating query definition and state modification.
IEnumerable<string> items = myCollection.SelectMany(a => a.myList);
foreach(string b in items)
{
MyHash.Add(b);
}
If you want to one-line it for some reason:
myCollection.SelectMany(a => a.myList).ToList().ForEach(b => MyHash.Add(b));

Adding IEnumerable<Type> to IList<Type> where IList doesn't contain primary key - LAMBDA

I have an IList<Price> SelectedPrices. I also have an IEnumerable<Price> that gets retrieved at a later date. I would like to add everything from the latter to the former where the former does NOT contain the primary key defined in the latter. So for instance:
IList<Price> contains Price.ID = 1, Price.ID = 2, and IEnumerable<Price> contains Price.ID = 2, Price.ID = 3, and Price.ID = 4. What's the easiest way to use a lambda to add those items so that I end up with the IList containing 4 unique Prices? I know I have to call ToList() on the IList to get access to the AddRange() method so that I can add multiple items at once, but how do I select only the items that DON'T exist in that list from the enumerable?
I know I have to call ToList() on the IList to get access to the AddRange() method
This is actually not safe. This will create a new List<T>, so you won't add the items to your original IList<T>. You'll need to add them one at a time.
The simplest option is just to loop and use a contains:
var itemsToAdd = enumerablePrices.Where(p => !SelectedPrices.Any(sel => sel.ID == p.ID));
foreach(var item in itemsToAdd)
{
SelectedPrices.Add(item);
}
However, this is going to be quadratic in nature, so if the collections are very large, it may be slow. Depending on how large the collections are, it might actually be better to build a set of the IDs in advance:
var existing = new HashSet<int>(SelectedPrices.Select(p => p.ID));
var itemsToAdd = enumerablePrices.Where(p => !existing.Contains(p.ID));
foreach(var item in itemsToAdd)
{
SelectedPrices.Add(item);
}
This will prevent the routine from going quadratic if your collection (SelectedPrices) is large.
You can try that:
var newPrices = prices.Where(p => !SelectedPrices.Any(sp => sp.ID == p.ID));
foreach(var p in newPrices)
SelectedPrices.Add(p);
I know I have to call ToList() on the IList to get access to the AddRange() method so that I can add multiple items at once
ToList will create a new instance of List<Price>, so you will be modifying another list, not the original one... No, you need to add the items one by one.
Try yourEnumerable.Where(x => !yourList.Any(y => y.ID == x.ID)) for the selection part of your question.
If you want to add new elements to the existing list and do that in a most performant way you should probably do it in a conventional way. Like this:
IList<Price> selectedPrices = ...;
IEnumerable<Price> additionalPrices = ...;
IDictionary<int, Price> pricesById = new Dictionary<int, Price>();
foreach (var price in selectedPrices)
{
pricesById.Add(price.Id, price);
}
foreach (var price in additionalPrices)
{
if (!pricesById.ContainsKey(price.Id))
{
selectedPrices.Add(price);
}
}

Conditional LINQ query in foreach

I have a multiselection box and I would like to iterate over the selected items and do a linq query, but I'm not sure how to write it. This is what I have so far:
if (lbStateLegislation.Items.Count > 0)
{
foreach (ListItem li in lbStateLegislation.Items)
attributes = vdc.attributes.Where(a => a.fieldvalue == li.Value).ToList();
}
I basically need to construct an OR query, so it is selecting from the collection where there are values for each of the selected items. I think, as it's written now, it is doing an AND query.
Just use the Contains extension method. Linq2Sql will translate it as an IN clause:
var inValues = lbStateLegislation.Items.Select(s => s.Value);
vdc.attributes.Where(a => inValues.Contains(a.fieldvalue));
You may be able to combine the two statements into one, but I will leave that for you to try as I'm not positive that it will work as a single statement.
HTH
var attributes=
vdc.attributes.Where(q=>
lbStateLegislation.Items.Any(o=> o.Value == q.fieldValue))
.Select(o=> o);

Categories