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();
}
Related
I am looping through a array of datarows and when a particular random item is not valid I want to remove that item and get the new total to get another random item.
But when I delete a datarow the datarow does not go away... And yes there is probably a much better way to do this but I am not smart enough to do it..
Instead of removing the row I see this inside
ItemArray = podLps[1].ItemArray threw an exception of type System.Data.RowNotInTableException
//PHASE 1: Get all LPs in the pod and add to collection
List<DataRow> allLps = dtLp.AsEnumerable().ToList();
DataRow[] podLps = allLps.Where(x => x.ItemArray[0].ToString() == loPod).ToArray();
//PHASE 2: Pick a random LP from collection that has valid WAVE1
for (int i = podLps.Count(); i > 0; i--)
{
//Recount items in collection since one may have been removed
int randomIndex = random.Next(podLps.Count());
var randomLpUserId = podLps[randomIndex].ItemArray[1].ToString();
var randomLpWave1 = int.Parse(podLps[randomIndex].ItemArray[2].ToString());
//Get WAVE1 # for selected LP
lpNumberOfLoans = GetNumberOfLoans(session, randomLpUserId);
//check if LP has valid WAVE1 then use this person
if (randomLpWave1 > lpNumberOfLoans)
{
return randomLpUserId;
}
else
{
podLps[randomIndex].Delete();
}
}
look at this example and it should point you in the right direction for removing rows I just tested it and it works
for (int i = myDataTable.Rows.Count - 1; i >= 0; i--)
{
DataRow row = myDataTable.Rows[i]; //Remove
if (myDataTable.Rows[i][0].ToString() == string.Empty)
{
myDataTable.Rows.Remove(row);
}
}
I would suggest to use a List for podLps instead of an array.
Then you can use .RemoveAt as Jaco mentioned (dosn't work for arrays).
DataRow.Delete() just flags the Row to be deleted in the next update of your DataTable.
The easiest method is to convert your array of DataRow[] to a List, call RemoveAt and then convert the list back to an array:
var dest = new List<>(podLps);
dest.RemoveAt(randomIndex);
podLps = dest.ToArray();
Collection was modified; enumeration operation may not execute.
I get that error when I run my program.
I have a dictionary and when I modify it I can't enumerate from it.
public void Render() {
foreach (var pair in players ) {
Main.SpriteBatch.DrawString(Main.BigFont, pair.Key, pair.Value, Color.White);
}
}
I tried modifying it at the value or removing it completely, then it must re-add it.
byte[] data = client.EndReceive(ar, ref ip);
string str_data = Encoding.ASCII.GetString(data);
string[] coords = str_data.Split(";".ToCharArray());
if (players.ContainsKey(coords[0])) {
players.Remove(coords[0]);
}
players.Add(coords[0], new Vector2(Int32.Parse(coords[1]), Int32.Parse(coords[2])));
You can also try to use an ordinary for loop instead of a foreach.
That way you can circumvent the warning, since you're not using the enumeration option.
For a dictionary you would do:
List<string/*or other datatype*/> keys = players.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
Main.SpriteBatch.DrawString(Main.BigFont, keys[i], players[keys[i]], Color.White);
}
Does this suit your needs?
var ToBeDeleted = new List<player>(); //Or whatever type was the list players holding
if (players.ContainsKey(coords[0])) {
ToBeDeleted.Add(coords[0]);
}
foreach(var item in ToBeDeleted)
{
players.Remove(item);
}
You cannot modify (add, update or remove) any collection while you are iterating over it. It is restricted in c#.
You need to modify your code so that you keep any updation on the list seperate. You can keep a seperate record for any changes that you have made during the loop and merge after the loop.e.g
var itemsToDelete = new Dictionary<int,Player>();
then change your this line
if (players.ContainsKey(coords[0])) {
players.Remove(coords[0]);
}
to
if (players.ContainsKey(coords[0])) {
itemsToDelete.Add(coords[0]);
}
then after your loop
foreach (var key in itemsToDelete)
{
players.Remove(key);
}
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));
}
}
I want to delete all datatables in a dataset apart from one. I've tried this :-
foreach (DataTable table in DtSet.Tables)
{
if (table.TableName != "tblAccounts")
{
DtSet.Tables.Remove(table);
}
}
but I get a
"Collection was modified; enumeration operation may not execute."
error
.
You cannot modify a collection during enumeration. But you could use a for-loop:
for (int i = DtSet.Tables.Count - 1; i >= 0; i--)
{
var table = DtSet.Tables[i];
if (table.TableName != "tblAccounts")
DtSet.Tables.Remove(table);
}
Just throwing this out there as it is untested but has no loops.
var accounts = DtSet.Tables["tblAccounts"];
DtSet.Tables.Clear();
DtSet.Tables.Add(accounts);
You'll get this anytime you try to modify a collection during a foreach. It has to do with the way the collection is iterated through.
You can do it a couple ways - one way is to determine the tables you want to remove while you're in the loop, then afterward, go back and remove them. I think the following will work:
var tablesToRemove = new List<DataTable>();
foreach (DataTable table in DtSet.Tables)
{
if (table.TableName != "tblAccounts")
{
tablesToRemove.Add(table);
}
}
foreach (DataTable table in tablesToRemove)
{
DtSet.Tables.Remove(table);
}
I get the following error while I try to delete a row while looping through it.
C#: Collection was modified; enumeration operation may not execute
I've been doing some research for a while, and I've read some similar posts here, but I still haven't found the right answer.
foreach (DataTable table in JobsDS.Tables)
{
foreach (DataRow row in table.Rows)
{
if (row["IP"].ToString() != null && row["IP"].ToString() != "cancelled")
{
string newWebServiceUrl = "http://" + row["IP"].ToString() + "/mp/Service.asmx";
webService.Url = newWebServiceUrl;
string polledMessage = webService.mpMethod(row["IP"].ToString(), row["ID"].ToString());
if (polledMessage != null)
{
if (polledMessage == "stored")
{
removeJob(id);
}
}
}
}
}
any help would be greatly appreciated
Instead of using foreach, use a reverse for loop:
for(int i = table.Rows.Count - 1; i >= 0; i--)
{
DataRow row = table.Rows[i];
//do your stuff
}
Removing the row indeed modifies the original collection of rows. Most enumerators are designed to explode if they detect the source sequence has changed in the middle of an enumeration - rather than try to handle all the weird possibilities of foreaching across something that is changing and probably introduce very subtle bugs, it is safer to simply disallow it.
You cannot modify a collection inside of a foreach around it.
Instead, you should use a backwards for loop.
If you want to remove Elements from a loop on a list of Elements, the trick is to use a for loop, start from the last Element and go to the first Element.
In your example :
int t_size = table.Rows.Count -1;
for (int i = t_size; i >= 0; i--)
{
DataRow row = table.Rows[i];
// your code ...
}
Edit : not quick enough :)
Also, if you depend on the order that you process the rows and a reverse loop does not work for you. You can add the rows that you want to delete to a List and then after you exit the foreach loop you can delete the rows added to the list. For example,
foreach (DataTable table in JobsDS.Tables)
{
List<DataRow> rowsToRemove = new List<DataRow>();
foreach (DataRow row in table.Rows)
{
if (row["IP"].ToString() != null && row["IP"].ToString() != "cancelled")
{
string newWebServiceUrl = "http://" + row["IP"].ToString() + "/mp/Service.asmx";
webService.Url = newWebServiceUrl;
string polledMessage = webService.mpMethod(row["IP"].ToString(), row["ID"].ToString());
if (polledMessage != null)
{
if (polledMessage == "stored")
{
//removeJob(id);
rowsToRemove.Add(row);
}
}
}
}
rowsToRemove.ForEach(r => removeJob(r["ID"].ToString()));
}
Somehow removeJob(id) changes one of the IEnumerables your enumerating (table.Rows or JobsDS.Tables, from the name of the method I guess it would be the latter), maybe via DataBinding.
I'm not sure the backwards for is going to work directly because it seems you're removing an element enumerated in the outer foreach from within the inner foreach. It's hard to tell without more info about what happens in removeJob(id).