This question already has answers here:
Collection was modified; enumeration operation may not execute
(16 answers)
Closed 4 years ago.
I am trying to remove one item from the list until the list is empty. My code only successfully removes one item from the list and then cause an error. How do I fix this?
public void ReleaseAllAnimals()
{
int i = 0;
foreach (var value in _farmAnimals)
{
_farmAnimals.RemoveAt(i);
Console.WriteLine(value.Species());
i++;
}
}
You should not be modifying the collection while enumerating it. As you want to remove elements you should use for loop instead with indexer so that you do not face the issue.
You can loop through the list in reverse order like and remove from the end item one by one:
for(int i=_farmAnimals.Count -1; i >= 0; i--)
{
var species = _farmAnimals[i].Species(); // get species of current item
_farmAnimals.RemoveAt(i); // remove from list
Console.WriteLine(species); // display it
}
You can remove each item from the first of your List by using RemoveAt(0)
Change your code to this:
int size = _farmAnimals.Count;
for(int i = 0; i < size; i++)
{
var s = _farmAnimals[0].Species();
Console.WriteLine(s);
_farmAnimals.RemoveAt(0);
}
I hope it helps you
Related
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
int count = 0;
foreach (string s in Settings.Default.Name)
{
count++;
}
Settings.Default.Name[count] = tb_add_name.Text;
Settings.Default.Save();
Settings.Default.Name is an empty string array but should the foreach - method just dont start if the string array is empty instead of giving me this error?
The array will be filled with words later.
Yes, but that won't change the fact that count is still 0 and you still execute
Settings.Default.Name[count] = tb_add_name.Text;
So you should still check if the index is Valid or null. Something like:
if(Settings.Default.Name != null && Settings.Default.Name.Count > 0)
By the way, your method will always lead to an IndexOutOfRange exception because your foreach loop basically sets your count variable to the size of the Array, and Array[Array.Length] is always out of range.
You can use Array Length property.
if(Settings.Default.Name.Count > 0)
{
int count = 0;
foreach (string s in Settings.Default.Name)
{
count++;
}
Settings.Default.Name[count] = tb_add_name.Text;
Settings.Default.Save();
}
I'm reading in a text file that contains data for 3D elements and store them in a dictionary dict in C#. The main objects are OPEN_SHELLs and CLOSED_SHELLs. These contain multiple ADVANCED_FACEs. These again contain a single FACE_OUTER_BOUND and multiple FACE_BOUNDs. These again contain more values and so on until there are finally numerical values.
For now I have a class Step that contains
List<List>string>> closedShell; //contains all closed shells with their values
List<List<string>> openShell; //contains all open shells with their values
List<List<string>> closedShellAdvFace; //contains all closed advanced faces...
List<List<string>> openShellAdvFace; //contains all open advanced faces...
...
I iterate through each list to get the next values and so on. Now this doesn't seem really efficient as I'm using duplicate code for closed and open lists.
An examplary code for this:
string closedShellKey = "";
string closedShellValue = "";
string openShellKey = "";
string openShellValue = "";
// For CLOSED_SHELLs
for (int shellListIndex = 0; shellListIndex < stepObj.GetClosedShells().Count; shellListIndex++)
{
for (int valuesCount = 1; valuesCount < stepObj.GetClosedShells()[shellListIndex].Count - 1; valuesCount++)
{
if (dict.ContainsKey(stepObj.GetClosedShells()[shellListIndex][valuesCount]))
{
closedShellKey = stepObj.GetClosedShells()[shellListIndex][valuesCount];
dict.TryGetValue(closedShellKey, out closedShellValue);
stepObj.SetCsAdvFace(SplitValues(closedShellValue));
} else
{
//Throw Exception
}
}
}
// For OPEN_SHELLs
for (int shellListIndex = 0; shellListIndex < stepObj.GetOpenShells().Count; shellListIndex++)
{
for (int valuesCount = 1; valuesCount < stepObj.GetOpenShells()[shellListIndex].Count - 1; valuesCount++)
{
if (dict.ContainsKey(stepObj.GetOpenShells()[shellListIndex][valuesCount]))
{
openShellKey = stepObj.GetOpenShells()[shellListIndex][valuesCount];
dict.TryGetValue(openShellKey, out openShellValue);
stepObj.SetOsAdvFace(SplitValues(openShellValue));
} else
{
//Throw Exception
}
}
}
This goes on for the next values, etc.
What would be a really good and efficient way to implement each of these steps?
Maybe create an openShellObject and a closedShellObject to further seperate?
How would I handle data that contains different data that again contains further different data, etc.?
Hope this is clear enough
First, note that Dictionary.TryGetValue already does the work of Dictionary.ContainsKey so you only need the former.
If I understand this, you need to iterate over multiple collections, applying an operation that varies only in one step, according to each collection category, e.g. closed face, open face, etc. How to separate that step from the iteration code? I'd suggest either Template Method pattern or Method As Parameter (MAP). For this problem I'd probably choose MAP because the collection items vary by category not data type, and probably it means less coding.
In the pseudocode below I've assumed that the final step in each iteration always involves a method like stepObj.SetCsAdvFace that takes a string[] value returned by SplitValues. This is why, in the Apply method below, the parameter method is a delegate that takes a string[] parameter, so it matches either stepObj.SetCsAdvFace or stepObj.SetOsAdvFace, whichever is required for the relevant collection.
private void Apply(List<List<string>> list, Action<string[]> method)
{
foreach (var items in list)
{
for (int valuesIndex = 1; valuesIndex < items.Count - 1; valuesIndex++)
{
var key = items[valuesIndex];
string values;
if (dict.TryGetValue(key, out values))
{
method(SplitValues(values));
}
else
{
//Throw Exception
}
}
}
}
Apply(stepObj.GetClosedShells(), stepObj.SetCsAdvFace);
Apply(stepObj.GetOpenShells(), stepObj.SetOsAdvFace);
...
First get rid of two for loops, use IEnumerable instead:
var allShellKeys = stepObj.GetClosedShells().Union(stepObj.GetOpenShells()).SelectMany(i => i.Skip(1).Take(i.Count() - 2))
Then you can iterate over all values in one loop:
string anyShellValue;
foreach (var anyShellKey in allShellKeys)
{
if (dict.TryGetValue(anyShellKey, out anyShellValue))
{
stepObj.SetCsAdvFace(SplitValues(anyShellValue));
}
else
{
//Throw Exception
}
}
I have made a method that eliminates any replicates of the same string in a List.
now, the problem is that it gives me this error:
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
I read in the internet, and i think that the problem is the i am removing an object from the list inside the foreach loop of the list.
foreach (string r in list)
{
int numberOfAppearance=0;
foreach (string rs in list)
{
if (r == rs && numberOfAppearance> 0)
list.Remove(rs);
else
numberOfAppearance++;
}
}
How can i fix the method? Thanks for the help
Firstly, as noted in comments, LINQ has got you covered here:
list = list.Distinct().ToList();
It's well worth looking into LINQ for data operations - it can make things much simpler.
As for what's wrong with your current code - there are a couple of things:
Firstly, you're removing by item rather than by index, which will remove the first occurrence of that item, not the one you're actually looking at
Secondly, if you modify a list while you're iterating over it, you will get precisely the exception you've seen. From the docs for List<T>.GetEnumerator:
An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.
You can get around this by iterating by index rather than using a foreach loop, but if you're removing an item you need to remember that everything below that will move up one element. So either you need to iterate backwards to remove items, or you need to remember to decrement the index.
Here's an approach which uses iterating by index forwards in terms of what we're looking at, but backwards in terms of looking for duplicates - stopping when we get to the index we're looking at. Note that this is still O(N2) - it's not as efficient as using Distinct:
// We're looking for duplicates *after* list[i], so we don't need to go as far
// as i being the very last element: there aren't any elements after it to be
// duplicates. (We could easily still just use list.Count, and the loop for j
// would just have 0 iterations.)
for (int i = 0; i < list.Count - 1; i++)
{
// Go backwards from the end, looking for duplicates of list[i]
for (int j = list.Count - 1; j > i; j--)
{
if (list[j] == list[i])
{
list.RemoveAt(j);
}
}
}
(For more details on Distinct, see my Edulinq post on it.)
As many people point out, you can use the Distinct method for your particular problem.
However, the problem you are actually having is that you are trying to modify the list when you iterate over it, which will not end well.
//This will not work.
foreach (string rs in list)
{
if (some_test)
{
list.Remove(rs); //Because of this line.
}
}
If you want do do something similar to this you need to find a way around this problem. Often it involves making a new array.
For this examle you can do the following
List newList = new ArrayList();
foreach (string rs in list)
{
if (!some_test)
{
newList.add(rs);
}
}
If you really want to create a "remove duplicates" method I would have done it in this fashion (pseudocode):
Hash cache_hash = new Hash(default false)
List new_list = new List
foreach string s in list
{
if not cache_hash[s]
{
new_list.add(s)
cache_hash[s] = true
}
}
list = new_list
This method is Ω(N) , so it is fairly fast on even large lists.
This question already has answers here:
Remove element of a regular array
(15 answers)
Closed 9 years ago.
string[] columns
I want to delete the item on an index specified by a variable of type int.
How do I do this ?
I tried
columns.RemoveAt(MY_INT_HERE);
But apparently this does not works.
Array is immutable class, you can't change it, all you can do is to re-create it:
List<String> list = columns.ToList(); // <- to List which is mutable
list.RemoveAt(MY_INT_HERE); // <- remove
string[] columns = list.ToArray(); // <- back to array
May be the best solution is to redesign your code: change immutable array into List<String>:
List<String> columns = ...
columns.RemoveAt(MY_INT_HERE);
If you don't want to use linq you can use this function :
public string[] RemoveAt(string[] stringArray, int index)
{
if (index < 0 || index >= stringArray.Length)
return stringArray;
var newArray = new string[stringArray.Length - 1];
int j = 0;
for (int i = 0; i < stringArray.Length; i++)
{
if(i == index)continue;
newArray[j] = stringArray[i];
j++;
}
return newArray;
}
You use it like that : columns = RemoveAt(columns, MY_INT_HERE)
You can also make it to an extension method.
You cannot delete items in an array, because the length of a C# array is fixed at the time when it is created, and cannot be changed after that.
You can null out the corresponding element to get rid of the string, or use LINQ to produce a new array, like this:
columns = columns.Take(MY_INT_HERE-1).Concat(columns.Skip(MY_INT_HERE)).ToArray();
You need to add using System.Linq at the top of your C# file in order for this to compile.
However, using a List<string> would be a better solution:
List<string> columns;
columns.RemoveAt(MY_INT_HERE);
Try one of the following (depending on what you need):
columns[MY_INT_HERE] = null;
columns[MY_INT_HERE] = string.Empty;
...otherwise you'll just have to create a new array which has a length of 1 less than your current array, and copy the values over.
If you want something more flexible, you might use a something like a List<string>, where you can use RemoveAt()
Arrays are faster for the computer to work with but slower for a programmer. You will have to find that value with a loop or some other means, then set that position to null. You will end up with an empty space in the array. You could reallocate the array etc etc...
What is easier to use for relatively small amounts of data is a List. You can do myList.RemoveAt(100); and it will work nicely.
You can not delete it.You can recreate the array or I advice you to use List<string> for the same.
List<string> columns = new List<string>();
columns.RemoveAt(1);
It will remove the 2nd element from your List<String> columns
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Remove element of a regular array
I have a method defined which returns class array.
ex: Sampleclass[]
The Sampleclass has properties Name, Address, City, Zip. On the client side I wanted to loop through the array and remove unwanted items. I am able to loop thru, but not sure how to remove the item.
for (int i = 0; i < Sampleclass.Length; i++)
{
if (Sampleclass[i].Address.Contains(""))
{
**// How to remove ??**
}
}
Arrays are fixed size and don't allow you to remove items once allocated - for this you can use List<T> instead. Alternatively you could use Linq to filter and project to a new array:
var filteredSampleArray = Sampleclass.Where( x => !x.Address.Contains(someString))
.ToArray();
It's not possible to remove from an array in this fashion. Arrays are statically allocated collections who's size doesn't change. You need to use a collection like List<T> instead. With List<T> you could do the following
var i = 0;
while (i < Sampleclass.Count) {
if (Sampleclass[i].Address.Contains("")) {
Sampleclass.RemoveAt(i);
} else {
i++;
}
}