1

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

Alexander Bell
  • 7,842
  • 3
  • 26
  • 42
SimpleGuy
  • 2,764
  • 5
  • 28
  • 45
  • @Plutonix Like this ? `for(inedx = this.dataTable.Columns.Count - 1 ; index >= 0 ; index++)` or is there is better way ? – SimpleGuy Jan 14 '15 at 02:39

4 Answers4

5

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);
}
Grant Winney
  • 65,241
  • 13
  • 115
  • 165
  • Thanks for the answer. In my case, as you said collection is small, but what if my collection was a large one ? Would there have been a way out to avoid copy ? – SimpleGuy Jan 14 '15 at 02:48
1
if (dt.Columns.Contains("RecordID")){
                    dt.Columns.Remove("RecordID");
                    dt.AcceptChanges();
                }
Power Mouse
  • 727
  • 6
  • 16
0

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);
    }

}
Abhishek
  • 2,925
  • 4
  • 34
  • 59
  • Well, I know that from the error. I am asking a way out. And the way you are suggesting, I have already pointed similar approach in my question and that does not sound good to me – SimpleGuy Jan 14 '15 at 02:45
0

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--;
                }
            }
Muflix
  • 6,192
  • 17
  • 77
  • 153