Iterating a list of objects with foreach - c#

I came across this statement:
"When using foreach on a list of objects, the iterated object instance is not editable, but the object properties are editable"
Could someone demonstrate the above with a simple example, please?
Let me re-phrase (as I found the statement in two versions), maybe this statement is more clear:
"When using foreach on a list of elements, the iteration variable that provides the element is readonly, but the element properties are editable"

foreach(var foo in foos)
{
foo = null; // WRONG, foo is not editable
foo.name = "John"; // RIGHT, foo properties are editable
}

What is means is that the items in the list can't change whilst iterating, but the contents of the items may.
this will alter the collection and prevent the foreach completing:
foreach(var item in collection)
{
collection.Remove(item);
}
This will change an item in the list and not prevent the foreach completing:
foreach(var item in collection)
{
item.name = "Neil";
}

Yes
Foreach (n in list) if (n.something==true) list.Remove(n);
this will fail
you cannot remove an item in list, unlike say a for loop

Not sure you need an example for this. You will step over each object in the collection, and you can do what you like to each of those objects, but you can't make changes to the collection itself, e.g. Insert, Remove, Clear, etc. Attempting such will throw an exception.

foreach var car in cars
{
//you can edit car.color here
//you cannot edit car
}

Related

How to edit an iterator within a foreach loop

Scenario
I have a system that holds races, each race has a unique list of members on that race. (the list is a List< T > )
I want users to be able to remove a member (if they ARE this member) from the list of members on that race.
Problem
I'm trying to get the following code to work:
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
item = null;
}
}
I can't edit the variable because it is in a foreach loop, how would I achieve this another way?
You can just store it and remove it form the collection afterwards.
var toRemove = null;
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
toRemove = item;
break; //Can break here if you're sure there's only one SelectedItem
}
}
hillracing.searchRaces(RaceID).Racelist.Remove(toRemove);
though in this case you could also just use hillracing.searchRaces(RaceID).Racelist.Remove(SelectedItem); and you won't use the foreach loop at all.
You can't modify collection that you are looping using foreach loop. The collection used in foreach is immutable. This is by design.
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects.
If you need to add or remove items from the source collection, use a
for loop.
Using Linq you shouldn't need to loop to find the entry you want to nullify...
// Use of Single() here assumes the object definitely exists.
// Use SingleOrDefaul() if there is a chance it might not exist.
var item = hillracing.searchRaces(RaceID)
.RaceList
.Where(x => x.Item == SelectedItem).Single();
item = null;
Edit: Since you've changed the requirement to remove the item from the list, I think you'd just call the Remove method`with the found item. So the code becomes
// Use of Single() here assumes the object definitely exists.
// Use SingleOrDefaul() if there is a chance it might not exist.
var item = hillracing.searchRaces(RaceID)
.RaceList
.Where(x => x.Item == SelectedItem).Single();
hillracing.searchRaces(RaceID).RaceList.Remove(item);
You can't do that in a foreach loop. If it's an IList/IList<T> which allows random access, like an array or list, you can use a for-loop:
List<string> = hillracing.searchRaces(RaceID).RaceList;
for(int i = 0; i < list.Count; i++)
{
if(list[i] == SelectedItem)
list[i] = null;
}
So you can't add or remove items in the foreach but you also can't replace the reference. The object refers to the original value so you could modify the object(if strings werent immutable) but you can't replace the reference itself in a foreach. This is related.
Use the existing Remove()-method to search and remove the item for you:
hillracing.searchRaces(RaceID).RaceList.Remove(SelectedItem);

How to edit a property that is deep within an object

Suppose there is a person object that has many ICollection
and ObjectType2 has Icollection
So to edit a property you could theoretically search deep with forloops.
But what would be a better way that is syntactically nice.
For example to edit a property called PP one could do the following:
foreach (var item in PersonData)
{
foreach (var item2 in item.Staffs)
{
foreach (var item3 in item2.Employees)
{
foreach (var item4 in item3.EmployeePositions)
{
item4.PP = "test";
}
}
}
}
But I am looking for something much nice such as:
Whether it via linq or whatever method.
var positions = PersonData
.SelectMany(p => p.Staffs)
.SelectMany(s => s.Employees)
.SelectMany(e => e.EmployeePositions);
foreach (var position in positions)
{
position.PP = "test";
}
This is the equivalent to the nested loops.
If these objects were stored in a database you would almost certainly do a query against the EmployeePositions table possibly filtering it by joining back against the Employees or Staff tables.
If you really need to access all instances of EmployeePositions you perhaps need a separate collection containing them rather than continually enumerating through the properties of other objects to find them.

Looping Observable Collection from the Last Item

foreach(var item in myObservalbleCollection)
{
//samoe coding
}
Above code will loop the items in the observable class from the beginning. I want to loop the this object from the last Item
ex 9,8,7,6,5,4,3,2,1,0 instead of 1,2,3,4,5,6,7,8,9...
Please give me a solution to do this in c#
Instead of using foreach you can use a for loop running from the end of the collection and to the start:
for (var i = myObservableCollection.Count - 1; i >= 0; i -= 1) {
var item = myObservableCollection[i];
// Process item
}
This is possible because ObservableCollection<T> implement IList<T> which provides both the count of the elements in the collection and indexed access to these elements in addition to the forward teration provided by IEnumerable<T> used by foreach. This is the most efficient solution.
You can also use LINQ Reverse which is available for any collection implementing IEnumerable<T>:
foreach (var item in myObservableCollection.Reverse()) {
// Process item
}
This will actually copy all the elements in the collection to a new array before iterating them in reverse order. While less efficient than the first solution it should not matter in most cases.
You could reverse the observablecollection and do the loop
var myObservalbleCollection = new ObservableCollection<YourType>(collection.Reverse());
foreach(var item in myObservalbleCollection)
{
//some coding
}

How add or remove object while iterating Collection in C#

I am trying to remove object while I am iterating through Collection. But I am getting exception. How can I achieve this?
Here is my code :
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(gem.Key); // I can't do this here, then How can I do?
OnGemCollected(gem.Value, Player);
}
}
foreach is designed for iterating over a collection without modifing it.
To remove items from a collection while iterating over it use a for loop from the end to the start of it.
for(int i = gems.Count - 1; i >=0 ; i--)
{
gems[i].Value.Update(gameTime);
if (gems[i].Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
Gem gem = gems[i];
gems.RemoveAt(i); // Assuming it's a List<Gem>
OnGemCollected(gem.Value, Player);
}
}
If it's a dictionary<string, Gem> for example, you could iterate like this:
foreach(string s in gems.Keys.ToList())
{
if(gems[s].BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(s);
}
}
The easiest way is to do what #IV4 suggested:
foreach (var gem in gems.ToList())
The ToList() will convert the Dictionary to a list of KeyValuePair, so it will work fine.
The only time you wouldn't want to do it that way is if you have a big dictionary from which you are only removing relatively few items and you want to reduce memory use.
Only in that case would you want to use one of the following approaches:
Make a list of the keys as you find them, then have a separate loop to remove the items:
List<KeyType> keysToRemove = new List<KeyType>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
OnGemCollected(gem.Value, Player);
keysToRemove.Add(gem.Key);
}
}
foreach (var key in keysToRemove)
gems.Remove(key);
(Where KeyType is the type of key you're using. Substitute the correct type!)
Alternatively, if it is important that the gem is removed before calling OnGemCollected(), then (with key type TKey and value type TValue) do it like this:
var itemsToRemove = new List<KeyValuePair<TKey, TValue>>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
itemsToRemove.Add(gem);
}
foreach (var item in itemsToRemove)
{
gems.Remove(item.Key);
OnGemCollected(item.Value, Player);
}
As the other answers say, a foreach is designed purely for iterating over a collection without modifying it as per the documenation:
The foreach statement is used to iterate through the collection to get
the desired information, but should not be used to change the contents
of the collection to avoid unpredictable side effects.
in order to do this you would need to use a for loop (storing the items of the collection you need to remove) and remove them from the collection afterwards.
However if you are using a List<T> you could do this:
lines.RemoveAll(line => line.FullfilsCertainConditions());
After going through all the answers, and being equally good. I faced a challenge where I had to modify a List and what I ended up doing worked quite well for me. So just in case anyone finds it useful. Can someone provide me feedback on how efficient it might be.
Action removeFromList;
foreach(var value in listOfValues){
if(whatever condition to remove is){
removeFromList+=()=>listOfValues.remove(value);
}
}
removeFromList?.Invoke();
removeFromList = null;
You should use the for loop instead of the foreach loop. Please refer here
Collections support foreach statement using Enumarator. Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException.
Use for loop for collection modifying.

work around to replace item in foreach loop

In my example code below i would like to replace the item in the dictionary with a new item or assign new values to that item. How can i do this?
This is my code:
dynamic data = jss.Deserialize<dynamic>(jsonText);
foreach (KeyValuePair<string,object> item in data["typeData"])
{
if(item.Key == "somevalue")
{
item = new KeyValuePair<string,object>();
}
}
I'm getting:
Cannot assign to 'item' because it is a 'foreach iteration variable'
There must be a work-around.
foreach are considered to be read only contexts.
Don't use foreach as the message says, convert the loop to a regular for loop.
From MSDN:
This error occurs when an assignment to variable occurs in a read-
only context. Read-only contexts include foreach iteration variables,
using variables, and fixed variables. To resolve this error, avoid
assignments to a statement variable in using blocks, foreach
statements, and fixed statements.
In your case the object item is not a reference its simply a copy hence any change you make to it will not result in a change in the original object.
Depends on what you want. Do you just need to override the value? I assume so because replacing the key and value would be a very different operation (remove one item and insert another)
just iterate over the keys instead of the collection (Assuming it's a dictionary):
dynamic data = jss.Deserialize<dynamic>(jsonText)["typeData"];
foreach (string key in data.Keys)
{
if(key == "somevalue")
{
data[key] = ...;
}
}
if there's no keys property you can substitute that part with (assuming that at least there's an indexer)
foreach (string key in data.Select(pair=>pair.Key)){
...
}
The problem with your code is that you are attempting to change the value of a variable that is used as a placeholder. The variable "item" simply has the same reference that exists in the dictionary; changing the object that "item" references won't actually change anything in the Dictionary itself, and on top of that it can screw up the logic of looping through the Dictionary.
In addition to not being able to reassign the placeholder, you are not allowed to add or remove items from the Dictionary within a foreach loop that uses said Dictionary, because that will also mess up the logic of iterating through the Dictionary's items (the item that is the "current" item of the enumerator behind the scenes now no longer exists, so the enumerator may lose its place in the collection and not be able to continue.
The workaround is to enumerate a different collection when you change the original collection. Basically, a task like this requires two passes; first collect the items you want to change, then enumerate through THAT collection and make the change to the original collection:
...
var itemsToChange = new List<KeyValuePair<string, object>>();
foreach (var item in data["typeData"])
{
if(item.Key == "somevalue")
itemsToChange.Add(item);
}
foreach(var item in itemsToChange)
{
//even here you can't just "swap out" KVPs;
//you must remove the old and add the new
data["typeData"].Remove(item);
data["typeData"].Add(someNewString, someNewObject);
}
You have to either use a for loop or store the variables you want changed and change them outside of the foreach loop.
Perhaps there's something missing from your question, but it seems that the workaround is to avoid looping entirely:
dynamic data = jss.Deserialize<dynamic>(jsonText);
var item = new KeyValuePair<string, object>("somevalue", data["somevalue"]);
or perhaps:
dynamic data = jss.Deserialize<dynamic>(jsonText);
DoSomethingWith(data["somevalue"]);
what's the reason for your loop?

Categories