How can I delete the current element of an array inside a foreach-loop?
My program gets data form a DB and sends it to a new one via HTTP requests. Now I want to post a JSON string to my new DB. If it was a success I want to delete the current array item which I'm working with. Something like this.
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == true)
{
//delete current item
}
}
since you cannot delete items from an array and change the size of it here is a loop approach using a second collection
List<itemClass> keepCollection = new List<itemClass>();
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == false)
{
keepCollection.Add(item);
}
}
If you need it again in array form just call ToArray()
var finalResult = keepCollection.ToArray();
appraoch with Linq which creates a new array with valid elements and overwrites the existing array
array = array.Where(x => !method.DoSomething(x)).ToArray(); //select valid elements
there are 2 ways (both were tested)
foreach (var item in array.ToList())
{
bool decide = method.DoSomething();
if (decide == true)
{
item.Remove();
}
}
and
for ( i=0; i < array.Length; i++)
{
bool decide = method.DoSomething();
if (decide == true)
{
array[i].Remove();
}
}
Whenever you want to delete entries from a collection, you should never loop through that collection from beginning to end, but always from end back to beginning.
By the way, C# does not allow you deleting entries from a collection while looping through that collection using a foreach loop.
Related
I am trying to modify some values within the value of the List collection List<DataClass>. The value which I am trying to modify listValues[i].destinationValue and listValues[i].sourceKey which is also a list collection. When I am trying to add another value to the collection it works using foreach loop. But when it goes to next value in the foreach loop value it stops and I get the error: System.InvalidOperationException: 'Collection was modified;.
foreach (var listValue in listValues)
{
if (listValue.hostName == data[4] && listValue.description == data[3] && listValue.ruleName == data[2])
{
var i = listValues.FindIndex(x => x.hostName.Equals(listValue.hostName));
listValues[i].destinationValue.Add(data[0]);
listValues[i].sourceKey.Add(data[1]);
}
else
{
listValues.Add(docValueModelClass);
}
}
if (listValues.Count == 0)
{
listValues.Add(docValueModelClass);
}
Can you please suggest, how can I overcome this?
You can't edit same list or same enumerating list inside foreach loop, Because we using same list for enumerable.
just use for loop to edit - It works.
I need to prevent Duplicate entries from ListView controller by column text. if duplicate found I need to get the ListView Item for further process. I saw every one says
ListViewItem item = ListView3.FindItemWithText("test");
if (!listView1.Items.ContainsKey(txt))
{
// doesn't exist, add it
}
but how can I point which Column text?? I did prevent duplicates by adding ids into a array and after check array value exists. but in that case I can find which entry duplicated.
this is my code.
rd = cmd.ExecuteReader();
// Validation not working - duplicating ListviewItems
while (rd.Read()) {
ListViewItem lvvi = new ListViewItem(rd.GetString(0));
lvvi.SubItems.Add(rd.GetString(1));
lvvi.SubItems.Add(rd.GetString(5));
lvvi.SubItems.Add("1");
lvvi.SubItems.Add(rd.GetString(0));
int listViewItemID;
int[] ids;
ids = new int[100];
if (listView3.Items.Count > 0)
{
int addingItemID;
//ADD ListView ids into array
int i=0;
foreach (ListViewItem li in listView3.Items)
{
listViewItemID = Int32.Parse(li.SubItems[0].Text);
addingItemID = Int32.Parse(rd.GetString(0));
ids[i] = listViewItemID;
i++;
}
//Check item allready exsist
if (ids.Contains(Int32.Parse(rd.GetString(0))))
{
MessageBox.Show("sdsd");
}
else {
listView3.Items.Add(lvvi);
}
}
else {
listView3.Items.Add(lvvi);
}
}
//Calculate Price summery
this.calculatePrice();
Instead of looping to get all id's, you can loop through the items or use linq to find the specific id and keep the result. This can be done in an external function or by replacing the ids portion with the loop or use something like FirstOrDefault:
addingItemID = rd.GetString(0);
ListViewItem existing = listView3.Items.Cast<ListViewItem>().FirstOrDefault(li => li.SubItems[0].Text == addingItemID); //(not sure if the cast is needed)
if (existing != null)
{
//item exists, variable existing refers to the item
MessageBox.Show("sdsd");
}
else
{
listView3.Items.Add(lvvi);
}
I want to iterate over a custom list i.e. defined as:
List<CurrentCluster> _curClusters = new List<CurrentCluster>();
IEnumerator<CurrentCluster> _clusIterate = _curClusters.GetEnumerator();
while (_clusIterate.MoveNext())
{
// Error_01: Cannot implicitly convert CurrentCluster to Cluster
Cluster _curClus = _clusIterate.Current; // Cluster is base class while
// CurrentCluster is derived class
// Error_02: Does not contain a definition for GetClusterSize()
if (_curClus.GetClusterSize() == 0)
{
// Error_03: Remove(char) has some invalid arguments.
_clusIterate.ToString().ToList().Remove(_curClus);
}
}
while method GetClusterSize() is defined in class Cluster.cs as:
public int GetClusterSize()
{
return _clusterObjects.Count;
// _clusterObjects is a defined in this class as:
// List<EvoObject> _clusterObjects = new List<EvoObject>();
}
If the size of specific cluster is equal to zero in that cluster list (i.e. _curClusters then to remove that cluster from the list.
How can we iterate over a custom list and remove item from list conditionally?
How about just using List RemoveAll method and doing this?
_curClusters.RemoveAll(_curClus=>_curClus.GetClusterSize() == 0);
You should be able to use a for loop - you have to work backwards because otherwise you would be moving the elements and some would get skipped.
for (int n=_curClusters.Count; n>=0; n--)
{
if (_curClusters[n].GetClusterSize()==0)
{
_curClusters.RemoveAt(n);
}
}
Removing items from a collection using iteration is both advanced and obsolete technique. Use LINQ instead:
_curClusters = _curClusters.Where(c => c.GetClusterSize() > 0).ToList();
Now curClusters contains just "sized clusters", whatever that means.
If you insist to do it through iterations this is the way:
The catch is that you MUST NOT change a collection while iterating over its items. Instead, you can iterate and determine if an item needs to be deleted and mark it somehow - for instance you can add it to another list which contains only items to be deleted. After the first iteration over the original collection, start second one and remove the items from the original, like so:
var toBeRemoved = new List<CurrentCluster>();
foreach (var suspiciousCluster in _curCluseters)
{
if(suspiciousCluster.GetClusterSize() == 0)
{
toBeRemoved.Add(suspiciousCluster);
}
}
foreach (var voidCluser in toBeRemoved)
{
_curCluster.Remove(voidCluster);
}
Again, _curClusters contains just "sized clusters", whatever this might mean.
However I highly recommend the first approach.
I did not understand why you are going with that complexity ... simply you can achieve the goal by below code
List<CurrentCluster> _curClusters = new List<CurrentCluster>();
_curClusters.RemoveAll(i => i.GetClusterSize()== 0);
//OR
for (int i = 0; i < _curClusters.Count; )
{
//If you have some more logical checking with CurrentCluster
//before remove
if (_curClusters[i].GetClusterSize()== 0)
{
_curClusters.Remove(_curClusters[i]);
continue;
}
i++;
}
Even after the RemoveAt() method, my list keeps being the same and I don't even get an error:
foreach (var row in queryCandidates.ToList())
{
try
{
xString = queryCandidates.ToList().ElementAt(i).District;
int.TryParse(xString, out xNumber);
temp = xNumber.Equals(districtNumber);
System.Diagnostics.Debug.Write(temp+ " ");
System.Diagnostics.Debug.Write(i+" ");
if (temp == false)
{
System.Diagnostics.Debug.WriteLine(" i is:"+i);
//not working even when it should
queryCandidates.ToList().RemoveAt(i);
}
}
catch { }
i++;
if (last == i)
{
System.Diagnostics.Debug.WriteLine("before ending loop: ");
return View(queryCandidates.ToList());
}
}
System.Diagnostics.Debug.WriteLine("after ending the loop: ");
return View(queryCandidates.ToList());
ToList() creates a new instance. From this instance you are removing the element. You are not removing the element from the original enumerable.
You should be doing something like this instead:
var candidates = queryCandidates.ToList();
var elementsToRemove = new List<int>();
foreach (var row in candidates)
{
// ...
xString = candidates[i].District;
// ...
if (temp == false)
{
// ...
elementsToRemove.Add(i);
}
}
for(int i = elementsToRemove.Count - 1; i >= 0; --i)
candidates.RemoveAt(elementsToRemove[i]);
return View(candidates);
Please note the use of elementsToRemove. You can't remove the items directly in the loop. This will throw an exception.
Additionally, please note that ToList copies all data. Every single time you call it. It should be obvious that this is not a good idea to do in a loop.
queryCandidates.ToList().RemoveAt(i);
ToList() creates a brand new list, which you then remove an element from, but that list is long gone.
Try:
var newList = queryCandidates.ToList();
for (int i=newList.Count-1; i>=0; i--){
///snip
newList.RemoveAt(i);
Note that I changed your foreach to for (in reverse) because you cannot modify a list while you are iterating over it with foreach.
The ToList() function creates a new List every time you call it. The object is removed from that list, not from the original list. So you should call ToList once before the foreach.
Once you've done that the removeAt() call will work and cause new issues because then you are trying to modify the list from within the foreach loop. So you'll need to rewrite your code in a way which takes the remove out of the loop as well.
Well I'm not exactly sure what Type queryCandidates is, but the reason you are not seeing an update is because you are removing element 'i' from the wrong object. Your ToList() function creates a new object of List type. If you want to keep the change you need to cache that list and use it where you use your original queryCandidates object.
queryCandidates isn't a list.
You're converting it to a list which creates a new instance from which you're removing the item but doesn't affect queryCandidates itself.
You can do:
var queryCandidates myCollection.ToList();
and then
queryCandidates.RemoveAt(i);
What works for me is to remove from the bottom up:
for (int i = list.Count - 1; i > 0; i--)
{
if (list[i][0] == " " || list[i][3] == "0")
list.RemoveAt(i);
}
It makes sense that some items are missed after decreasing the item count.
I am trying to do this:
foreach (Settings sets in MySets)
{
if (sets.pName == item.SubItems[2].Text)
{
var ss = new SettingsForm(sets);
if (ss.ShowDialog() == DialogResult.OK)
{
if (ss.ResultSave)
{
sets = ss.getSettings();
}
}
return;
}
}
But since the sets spawned variable is readonly, I cant override it.
I would also like to do something like this
foreach (Settings sets in MySets)
{
if(sets.pName == someName)
sets.RemoveFromList();
}
How can I accomplish this? Lists have a very nice Add() method, but they forgot the rest :(
You can use:
MySets.RemoveAll(sets => sets.pName == someName);
to remove all the items that satisfy a specific condition.
If you want to grab all the items satisfying a condition without touching the original list, you can try:
List<Settings> selectedItems = MySets.FindAll(sets => sets.pName == someName);
foreach loops don't work here as trying to change the underlying list will cause an exception in the next iteration of the loop. Of course, you can use a for loop and manually index the list. However, you should be very careful not to miss any items in the process of removing an item from the list (since the index of all the following items will get decremented if an element is removed):
for (int i = 0; i < MySets.Count; ++i) {
var sets = MySets[i]; // simulate `foreach` current variable
// The rest of the code will be pretty much unchanged.
// Now, you can set `MySets[i]` to a new object if you wish so:
// MySets[i] = new Settings();
//
// If you need to remove the item from a list and need to continue processing
// the next item: (decrementing the index var is important here)
// MySets.RemoveAt(i--);
// continue;
if (sets.pName == item.SubItems[2].Text)
{
var ss = new SettingsForm(sets);
if (ss.ShowDialog() == DialogResult.OK)
{
if (ss.ResultSave)
{
// Assigning to `sets` is not useful. Directly modify the list:
MySets[i] = ss.getSettings();
}
}
return;
}
}
You can't do it in a 'regular' for loop?