How to reduce multiple nested foreach blocks - c#

I have the following scenario:
var Ids = object1.GetIds(); // returns IEnumerable<int>
foreach (var id in Ids)
{
foreach (var relatedObject in object1.GetRelatedObjects(id))
{
// Do Something with related object
}
}
In this case, i want to get rid of from the first foreach and reduce this logic into single foreach. How could i achieve this?
Should it be possible with LINQ expression some similar methodology?

When there is nothing between the two loops, before or after the nested one, you can use SelectMany to "flatten" two loops into one:
foreach (var relatedObject in Ids.SelectMany(object1.GetRelatedObjects)) {
...
}
One major difference between this loop and the loop that you have is that id is no longer in scope. Assuming that relatedObject exposes a public Id property, this should not be a problem in your situation, because you could extract the id back with
var id = relatedObject.Id;

Personally I like to take full advantage of the optional braces/block for foreach loops.
You can't reduce the complexity. But you can make it look nicer
IEnumerable<int> Ids = object1.GetIds()
foreach (var id in Ids)
foreach (var relatedObject in object1.GetRelatedObjects(id))
{
DoSomething(relatedObject);
}

Related

Searching for a string in HashSet<string> Performance

I have a HashSet<string> with ~50k members. I have another list of objects that I'm iterating through one by one to determine if the object's email exists. If it does, I need to perform some action on the object.
var emailList = db.Emails.Select(s => s.EmailAddress.ToLower()).ToList();
var emailHash = new HashSet<string>(emailList);
var objects = db.Objects.ToList();
// everything is fine up to this point
foreach (var object in objects) {
if (!emailHash.Any(s => s.Equals(object.Email))) { // This takes ~0.3s
Console.WriteLine("Email: {0}", object.Email);
}
}
What can I do to speed up the evaluation of whether or not one string exists in a list of strings?
You are not using the HashSet correctly. Using Linq's .Any() will actually evaluate your condition against each element stored in the HashSet.
To search if an item exists in a HashSet (with constant time, O(1)) use emailHash.Contains(object.Email).
One obvious change is to not use the Enumerable.Any() LINQ function, which basically negates the advantages of using a hash set by performing a sequential search.
Instead, use HashSet's built-in Contains(string) function:
foreach (var object in objects) {
if (!emailHash.Contains(object.Email)) {
Console.WriteLine("Email: {0}", object.Email);
}
}

Define a condition in a loop, without add an if

I have a loop that is going through each element of a list.
At the moment I have the foreach loop, and inisde, an if that check a variable for the element of the list. If the value is true, I do certain activities, otherwise I move on.
I was wondering if there is some sort of construct, that would allow me to write something like
foreach (myclass item in itemlist) where ( item.is_red)
{
... do something
}
instead than what I have currently
foreach (myclass item in itemlist)
{
if (item.is_red)
... do something
}
From what I recall, the "where" can be used only as constant definition mechanism, like when defining a specific type of class; so it can't be used in the context that I am presenting here. Am I stuck with the if-else inside the loop as only solution?
Convert the enumeration to List and use the Where and ForEach LINQ operator:
itemlist.Where(t => t.is_red).ToList().ForEach(t => DoSomething(t));
or
itemlist.Where(t => t.is_red).ToList().ForEach(t =>
{
// do something
}
You could use LINQ
foreach (myclass item in itemlist.Where(i => i.is_red))
{
//... do something
}
Yes, you could filter what you want iterate with LINQ Where in the collection first:
foreach (myclass item in itemlist.Where(x => x.is_red)) //here is the `Where`
{
//do your stuffs
}
Almost :)
foreach (var item in (from i in numbers where i.is_red select i))
{
... do something
}
There generally is a small performance hit when LINQ is used to iterate over lists or arrays, since an enumerator object is to be created.
Another way to tackle your problem is to use your condition as guard clause. This can reduce nesting and complexity and usually improves readability (although there is also the opinion of not using multiple returns/continues/breaks in a function/loop to keep cyclomatic complexity down):
foreach (var item in itemlist)
{
if (!item.is_red) { continue; }
... do something
}
In the end it probably boils down to company policy/personal preference.

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.

A single foreach statement which iterates over two collections

So I have two collections - one of int?, and one of string.
I want to iterate over both, and I have a good chunk of code that I want to perform to each. However there are some slight differences. if the value is an int?, some additional code is needed.
I'm trying to follow DRY principles, so I don't want to rewrite my code. I also don't want to put it in an external function, as this is already abstracted away from my MVC app controller code and I don't want it getting needlessly complex.
Is there a way to do something like this in c#
foreach(object o in (ints && strings))
{
if(o is int)
{
// do in specific stuff
}
// do all my generic stuff.
}
EDIT:
It's actually int? not int. when I try and combine them into a generic list of type List<object> I get an error
With this particular structure, you can combine them into one big object collection using Concat, like:
foreach (object o in ints.Cast<object>().Concat(strings.Cast<object>()))
But I'd probably suggest breaking out your functionality into a method, and running two loops:
foreach (int i in ints) {
DoSomething(i);
DoSomethingWithInt(i);
}
foreach (string s in strings) {
DoSomething(s);
DoSomethingWithString(s);
}
That will keep things clean and a little more readable in my opinion.
I think it would be cleaner to put the generic stuff in a common function, then loop over both collections. That way you get rid of the somewhat odd if int stuff.
foreach(int number in ints)
{
// do int specific stuff
GeneralFunction(number);
}
foreach(string stringValue in strings)
{
GeneralFunction(number);
}
This could also work for you:
IEnumerator en1 = ints.GetEnumerator();
IEnumerator en2 = strings.GetEnumerator();
while((en1.MoveNext()) && (en2.MoveNext()))
{
WhateverActionWithNullableInt((int?)en1.Current);
WhateverActionWithString((string)en2.Current);
}
Yes, you can do this:
List<int> ints = new List<int>();
List<string> strings = new List<string>();
foreach (object o in ints.Cast<object>().Concat(strings))
{
if (o is int)
{
// do in specific stuff
}
// do all my generic stuff.
}
I would separate it into functions and take advantage of overloading myself, but you said you didn't want to do that.
You should create a new list, which is the concatenation of the two lists :
EDIT
List<object> list = new List<object>();
List<int> ints = new List<int>(); ints.Add(74); ints.Add(47);
List<string> strings = new List<string>(); strings.Add("hello"); strings.Add("hello2");
list.AddRange(ints.Cast<object>());
list.AddRange(strings.Cast<object>());
foreach (object o in list)
{
Trace.WriteLine(o);
if (o is int)
{
// do in specific stuff
}
// do all my generic stuff.
}

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.

Categories