14

I have a method with a nested foreach collection (iterate a set of objects and then look inside each object). I saw in a book a good pattern to make this much more elegant but cannot remember/find the code example. How else could I make this more tidy?

The code is just a typical nested foreach statement so I have not provided a code sample.

2
  • 12
    This comment is just a typical comment, so I have not provided a comment! ;-) Commented Mar 12, 2010 at 15:25
  • 2
    I'm reminded of Eric Lippert's discussion of continuing to an outer loop: blogs.msdn.com/ericlippert/archive/2010/01/11/… Commented Mar 12, 2010 at 15:38

7 Answers 7

17

The obvious solution is to flatten into methods.

Old:

void SubmitOrders()
{
    var orders = GetOrders();
    foreach (Order o in orders)
    {
        foreach (OrderDetail d in o.Details)
        {
            // Blah...
        }
    }
}

New:

void SubmitOrders()
{
    var orders = GetOrders()
    foreach (Order o in orders)
    {
        SubmitOrder(o);
    }
}

void SubmitOrder(Order order)
{
    foreach (OrderDetail d in order.Details)
    {
        // Blah...
    }
}

Other answers here seem to be focused on Linq, and I would agree that if your loops have no side-effects (i.e. you are just trying to extract some information from the innermost loop), then you can probably rewrite the entire thing using one or two simple Linq statements. If side-effects are involved, then just follow the time-tested practice of subroutines.

Sign up to request clarification or add additional context in comments.

2 Comments

It was similar to this (returning IEnumerable). Thanks.
If it is recursive, it could cause stack overflow. Thought of using a queue for breadth first traversal?
16

You're going to have to be more specific about what you mean regarding "more elegant", as IMO there's nothing particularly inelegant about a nested foreach.

That being said, the LINQ extension methods in .NET 3.5 and above can help out (specifically SelectMany).

public class Foo
{
    public List<string> Strings { get; set; }
}

...

List<Foo> foos = new List<Foo>();

foreach(string str in foos.SelectMany(f => f.Strings))
{
    ...
}

Comments

8

Before:

foreach(Customer c in Customers)
{
  foreach(Order o in c.Orders)
  {
    o.Dance();
  }
}

After:

foreach(Order o in Customers.SelectMany(c => c.Orders))
{
  o.Dance();
}

Comments

1

You could use the LINQ SelectMany operator:

foreach (var bar in model.FooCollection.SelectMany(f => f.Bars))
{
  // Do Stuff
}

http://www.hookedonlinq.com/SelectManyOperator.ashx

Comments

1

Its difficult to give an answer without any context or code snippet. However, Martin Fowler has a really good article on refactoring loops into LINQ pipelines which can be found here (appreciate this is an old question though, so hopefully future readers will benefit!).

1 Comment

Good reference!
1

I'd suggest creating a Dictionary out of the second thing you loop through. Then inside the foreach just use your Dictionary as a lookup for the value you want.

Updated with examples embedded foreach and Dictionary alternative:

List<ItemA> itemAs = new List<ItemA> { 
    new ItemA { id = 1, number = 10 },
    new ItemA { id = 2, number = 15 },
    new ItemA { id = 3, number = 20 }
};
List<ItemB> itemBs = new List<ItemB> {
    new ItemB { id = 1, rate = 100 },
    new ItemB { id = 2, rate = 150 },
    new ItemB { id = 3, rate = 200}
};
List<ItemAB> itemABs = new List<ItemAB>();
// embedded foreach
foreach(ItemA itemA in itemAs)
{
    foreach(ItemB itemB in itemBs)
    {
        if(itemA.id == itemB.id)
        {
            ItemAB itemAB = new ItemAB { id = itemA.id, number = itemA.number, rate = itemB.rate };
            itemABs.Add(itemAB);
        }
    }
}
// using dictionary as lookup
Dictionary<int, ItemB> itemBLookup = itemBs.ToDictionary(x => x.id, y => y);
foreach (ItemA itemA in itemAs)
{
    itemBLookup.TryGetValue(itemA.id, out ItemB itemB);
    ItemAB itemAB = new ItemAB { id = itemA.id, number = itemA.number, rate = itemB.rate };
    itemABs.Add(itemAB);
}

3 Comments

I guess you mean a queue or stack instead of a dictionary.
Please look up graph traversal algorithm and then update your answer.
The scenario I'm thinking about is one in which you have two lists, and they match on some property, and you want to use properties from the objects in both lists to do something else, like build another list of objects with properties from both. I'll update my answer with an example.
0

Are you thinking of something like this?

public class YourObject{
   public List<OtherObject> Others { get; set; }
}

public class OtherObject{
   public void DoIt(){}
}

var theList = new List<YourObject>();

theList.ForEach(yo => yo.Others.ForEach(oo => oo.DoIt()));

2 Comments

ForEach is a method on List<T>, not LINQ.
@Adam Robinson: Though one could (very) easily write a ForEach extension method on IEnumerable<T> that works exactly like it does for List<T>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.