Changing collection inside foreach - c#

Here is my code:
foreach (OrderItem item in OrderInfo.order)
{
orderItemViews.Single(i => i.numericUpDown.Name == item.id.ToString()).numericUpDown.Value = item.count;
}
It gives an exception.
I know that I can't change the collection inside foreach
How can I change this code to make it work? Best of all if it would be LINQ code.
exception says that "The collection was modified". sorry can't provide real message of exception because it in non-english
sorry guys. I've found where collection is changing. It was inside *numericUpDown_ValueChanged* handler. anyway I've got an answer. thank you

You can use ToList(), Like this :
foreach (OrderItem item in OrderInfo.order.ToList())
{
orderItemViews.Single(i => i.numericUpDown.Name == item.id.ToString()).numericUpDown.Value = item.count;
}
Or use normal for loop :
for (int i = 0 ; i < OrderInfo.order.Count; i++)
{
OrderItem item = OrderInfo.order[i];
orderItemViews.Single(i => i.numericUpDown.Name == item.id.ToString()).numericUpDown.Value = item.count;
}
Tip : Performance wise, It's better to use the second way.

This is what I do, when I need to modify the collection.
foreach (OrderItem item in OrderInfo.order.ToList())
{
...
}
Create a copy. Enumerate the copy, but update the original one.

You can use an extension ToEach static method:
public delegate void ToEachTransformAction<T>(ref T Telement);
[Extension()]
public static void ToEach<T>(Generic.IList<T> thislist, ToEachTransformAction<T> Transform)
{
for (var n = 0; n < thislist.Count; n++)
{
Transform.Invoke(thislist(n));
}
}

Related

Remove column by name [duplicate]

I have a code
foreach (DataColumn dataTableCol in this.dataTable.Columns)
{
bool columnFound = false;
foreach (GRTColumnView uiColumn in descriptor.UIColumns)
{
if (dataTableCol.ColumnName.Equals(uiColumn.Name))
{
columnFound = true;
break;
}
}
if (!columnFound)
{
if (this.dataTable.Columns.Contains(dataTableCol.ColumnName))
this.dataTable.Columns.Remove(dataTableCol.ColumnName);
}
}
I want to remove some "things" from collection if they aren't found in another collection.
When I run the above program, I get
Iteration may not execute as collection was modified
"Collection was modified" - Coz the remove must have been hit
What is the way to achieve such a thing then ?
What I can think of is make a note of all the "things" to remove and then
foreach( aThing in all_things_to_remove)
remove_from_collection(aThing)
But above doesn't seem a good way to me, as I have to do another looping and am using extra memory
In this specific case, where you're looping through a small collection containing a few columns, you can just create a new collection (via ToList()), so that you're not iterating over the same collection you're modifying:
foreach (var dataTableCol in dataTable.Columns.Cast<DataColumn>().ToList())
{
...
dataTable.Columns.Remove(dataTableCol.ColumnName);
}
The recommended way, especially if the collection is large, is to enumerate backwards:
for (var i = dataTable.Columns.Count - 1; i >= 0; i--)
{
...
dataTable.Columns.Remove(dataTable.Columns[i].ColumnName);
}
You cannot remove items from a collection while enumerating it with the foreach loop. Make a copy of the collection using Collection.ToArray() and run you foreach on the copy and remove your item from the actual collection.
Since DataTable.Columns does not have ToArray or ToList methods, you could use the CopyTo() method and copy the entire columns to a ColumnsArray.
If you do not want to create a copy, then you can use for loop instead of foreach loop. You could edit you code this way:
for (int i = 0; i < dataTable.Columns.Count; i++)
{
bool columnFound = false;
foreach (GRTColumnView uiColumn in descriptor.UIColumns)
{
if (dataTable.Columns[i].Name.Equals(uiColumn.Name))
{
columnFound = true;
break;
}
}
if (!columnFound)
{
if (this.dataTable.Columns.Contains(dataTableCol.ColumnName))
this.dataTable.Columns.Remove(dataTableCol.ColumnName);
}
}
the other way is lowering the index after removing column.
for (int i = 0; i < datatable.Columns.Count; i++)
{
if (datatable.Columns[i].ColumnName.Contains("Column"))
{
datatable.Columns.RemoveAt(i);
i--;
}
}
if (dt.Columns.Contains("RecordID")){
dt.Columns.Remove("RecordID");
dt.AcceptChanges();
}

Comparing two large lists and assigning property values from another

I have a very simple method of comparing two lists and then assigning the value from first list to second like following:
private void FindUPC(List<ResultItem> filteredProducts, List<zsp_select_UserItems_Result> items)
{
foreach (var item in items)
{
foreach (var trItem in filteredProducts)
{
if (item.ItemID == trItem.ID)
{
trItem.UPC = item.UPC;
trItem.EAN = item.EAN;
trItem.MPN = item.MPN;
}
}
}
}
However, when doing it like this on large collections such as let's say comparing collection where each list contains 50000 items inside, the execution time that it takes here is a whooping 30000 miliseconds (30 seconds)... This impacts the performance terribly, so I'm wondering what is the fastest way to compare two lists and then assign the values like I've shown in my previous example that I wrote?
Can someone help me out?
P.S. Guys can I use IEqualityComparer for this?
#Stephen did you mean something like this:
private void FindUPC(List<ResultItem> filteredProducts, List<zsp_select_UserItems_Result> items)
{
foreach (var item in items)
{
foreach (var trItem in filteredProducts)
{
if (item.ItemID == trItem.ID)
{
trItem.UPC = item.UPC;
trItem.EAN = item.EAN;
trItem.MPN = item.MPN;
}
break;
}
}
}
Load all your items into a data structure with constant lookup. Then perform your loop.
private void FindUPC(List<ResultItem> filteredProducts, List<zsp_select_UserItems_Result> items)
{
var itemsDict = items.ToDictionary(i => i.ItemID);
foreach (var trItem in filteredProducts)
{
if (itemsDict.TryGetValue(trItem.ID, out var item)) {
trItem.UPC = item.UPC;
trItem.EAN = item.EAN;
trItem.MPN = item.MPN;
{
}
}

How to count itens in a list using foreach and one condition?

I have an 'foreach';
foreach (var item in Model.LstUnidadesGerenciais)
{
var count = Model.LstUnidadesGerenciais.Count;
}
I need to get count with one condition like this:
foreach (var item in Model.LstUnidadesGerenciais)
{
if (item.Level == 1)
{
var count = Model.LstUnidadesGerenciaisWITHCONDITION.Count;
}
}
I think this is simple, but I'm very begginer in C#
Thank you!
Using Linq
var cnt = Model.LstUnidadesGerenciais.Count(x=>x.Level==1);
You can use LINQ Where to select items matching criteria (Level == 1):
var count = Model.LstUnidadesGerenciais.Where(i => i.Level == 1).Count();
Use Count method of LINQ -
var count = Model.LstUnidadesGerenciais.Count(i => i.Level == 1);
In the first example, you don't need the foreach loop. You can tell, because you never use the item, you use the collection instead. I think you need to understand what foreach does here:
foreach (var item in Model.LstUnidadesGerenciais)
{
var count = Model.LstUnidadesGerenciais.Count;
}
foreach will iterate over Model.LstUnidadesGerenciais. This means that for every item in Model.LstUnidadesGerenciais, the code inside the curly brackets is executed, with var item containing the current item of the collection. See MSDN for detailed information
As for the second example: You need a variable that contains a number. In the foreach loop you can increment the variable like:
int count = 0;
foreach (var item in Model.LstUnidadesGerenciais)
{
if (item.Level == 1)
{
count++;
}
}

Select a control by custom property

I've been trying for a while but I'd like to modify a specific control's value without looping through all controls to check if a textbox's id property matches a correct value.
Currently this is the code I have but I thought perhaps using LINQ it's more efficient;
for (int i = 0; i < protectMaxPlayers; i++)
{
// Update the protect time.
protect.setProtectTime(i, protect.getProtectTime(i) - 1);
// Set the progressbar.
foreach (ProtectProgressBar pb in pnlProtect.Controls.OfType<ProtectProgressBar>())
{
if (pb.Id == i)
pb.Value = protect.getProtectTime(i);
}
}
}
This loops through ALL the progressbars in order to find the right one.
Is this possible to get shorter?
Thanks in advance.
LINQ will iterate over whole collection of ProgressBars as well, so it's not any better than your current solution.
You should consider preparing Dictionary<string, ProtectProgressBar> and using it to find the correct one using it's ID:
var bars = pnlProtect.Controls.OfType<ProtectProgressBar>().ToDictionary(c => c.Id, c => c);
for (int i = 0; i < protectMaxPlayers; i++)
{
// Update the protect time.
protect.setProtectTime(i, protect.getProtectTime(i) - 1);
ProtectProgressBar bar;
if(bars.TryGetValue(i, out bar))
{
bar.Value = protect.getProtectTime(i);
}
}
Dictionary<TKey, TValue> lookup is done in O(1) time, so it should be better then you current solution.
to accomplish this task, you must:
Import Linq namespace above.
import System.Linq;
Then use the code like that:
for (int i = 0; i < protectMaxPlayers; i++)
{
// Update the protect time.
protect.setProtectTime(i, protect.getProtectTime(i) - 1);
// Set the progressbar.
ProtectProgressBar pb = pnlProtect.Controls.OfType<ProtectProgressBar>().ToList().Find(k => k.ID == i.ToString());
// check if it was found
if (pb != null)
{
// your code
}
}
Regards, Wiliam.

How do I limit the number of elements iterated over in a foreach loop?

I have the following code
foreach (var rssItem in rss.Channel.Items)
{
// ...
}
But only want 6 items not all items, how can I do it in C#?
just iterate over the top 6 from the collection:
foreach(var rssItem in rss.Channel.Items.Take(6))
Not to be too obvious but...
int max = Math.Min(6, rss.Channel.Items.Count);
for (int i = 0; i < max; i++)
{
var rssItem = rss.Channel.Items[i];
//...
}
I know it's old school, and not filled with all sorts of Extension method goodness, but sometimes the old school still works... especially if you're still using .NET 2.0.
Use Enumerable.Take:
foreach(var rssItem in rss.Channel.Items.Take(6)) {
// go time!
}
Note that
rss.Channel.Items.Take(6)
does not do anything except instantiate an implementation of IEnumerable that can be iterated over to produce the first six items in the enumeration. This is the deferred execution feature of LINQ to Objects.
Note further that this assumes .NET 3.5. If you are working with an earlier version of .NET, you could use something along the lines of the following:
static IEnumerable<T> Take<T>(IEnumerable<T> source, int take) {
if (source == null) {
throw new ArgumentNullException("source");
}
if (take < 0) {
throw new ArgumentOutOfRangeException("take");
}
if (take == 0) {
yield break;
}
int count = 0;
foreach (T item in source) {
count++;
yield return item;
if (count >= take) {
yield break;
}
}
}
Then:
foreach(var rssItem in Take(rss.Channel.Items, 6)) {
// go time!
}
This assumes .NET 2.0. If you're not using .NET 2.0 you should seriously consider upgrading.
You could also just break out of the loop if you don't want to use linq.
int count = 0;
foreach (var rssItem in rss.Channel.Items)
{
if (++count == 6) break;
...
}
If you're interested in a condition (i.e. ordering by date created)
foreach(var rssItem in rss.Channel.Items.OrderByDescending(x=>x.CreateDate).Take(6))
{
//do something
}
Perhaps if you want to get those created by a certain user, with that same sort
foreach(var rssItem in rss.Channel.Items
.Where(x=>x.UserID == 1)
.OrderByDescending(x=>x.CreateDate)
.Take(6))
{
//do something
}
rss.Channel.Items.Take(6)

Categories

Resources