views:

135

answers:

5

I have 3 (Edit) mutually exclusive IEnumerables that I want to iterate over. I want to do something like this:

IEnumerable<Car> redCars = GetRedCars();
IEnumerable<Car> greenCars = GetGreenCars();
IEnumerable<Car> blueCars = GetBlueCars();

foreach(Car c in (redCars + greenCars + blueCars)) {
    c.DoSomething();
}

...

The best way I can think of is:

...
List<Car> allCars = new List();
allCars.AddRange(redCars);
allCars.AddRange(greenCars);
allCars.AddRange(blueCars);
foreach(car in allCars) {
    ...
}
...

Is there a more concise way to do this? Seems like combinding IEnumberables should be trivial.

A: 

Use LINQ Union method.

foreach(Car c in redCars.Union(greenCars).Union(blueCars))
{
    c.DoSomething();
}
Elisha
If you absolutely need to do this in one loop, I'd go with the LINQ.
Scott J
Didn't think of that. But won't this be expesive as it has to compare them all? Especially because the three groups are mutually exclusive.
sdr
For info, `Union` will be more expensive that `Concat` here, and could very well produce a different result to the `AddRange` approach (which is essentially a `Concat`).
Marc Gravell
@sdr, if the collections share no elements it's better to use Concat as Marc Garvell mentioned.
Elisha
@Mark, good info on Concat versus Union.
Anthony Pegram
+17  A: 

With LINQ:

foreach(car in redCars.Concat(greenCars).Concat(blueCars)) {
    //...
}

For info, the difference here between Union and Concat is that Union will do extra work to guarantee uniqueness; so if you don't expect duplicates (or alternatively: don't mind them) then Concat is faster.

Marc Gravell
Heh... I started with Concat and then went to Union and then decided I couldn't decide which to use, so I deleted my answer. I like yours explaining the difference better anyway. +1
Randolpho
Exactly what I was looking for. Thanks!
sdr
@sdr: Note that there is a time/space tradeoff here. Suppose you had not three but twenty such lists each with ten thousand elements. Your method (concatenate them all together into one big list) takes up around 20 x 10K extra references and takes 20 x 10K reference copy operations. Doing 20 naive concats, on the other hand, takes up zero extra references, but nested concats have a copying burden based on their depth of nesting, so there will be 10 x 11 x 10K = 110K copies, not 20K copies. See http://blogs.msdn.com/wesdyer/archive/2007/03/23/all-about-iterators.aspx for details.
Eric Lippert
@Eric - so any hope of getting a `yield foreach` construct in a hypothetical future version of C#?
kvb
A: 

Is there a reason you need to do it in one loop? If so, the second way is probably the best. There's nothing that will work like the first way.

Unless there's an overriding need to do it in one loop, I'd do it in three loops which would be more time efficient.

Scott J
I think the "more time efficient" is a big claim. Note also that creating a list (with allocations, copying, etc) has an overhead too. And there very much *is* something that will work like the first way.
Marc Gravell
@Scott, if he's working with each car the same way, why would he want more than one loop? That's just redundant code.
Anthony Pegram
A: 

I suggest .Union

List<int> list1 = new List<int>() { 1, 2, 3 };
List<int> list2 = new List<int>() { 4, 5, 6 };
List<int> list3 = new List<int>() { 7, 8, 9 };

IEnumerable<int> list = list1.Union(list2).Union(list3);

foreach (int i in list)
    Console.Write(i);
Anthony Pegram
+2  A: 

As noted in other answers, Union does extra work to eliminate duplicates, Concat simply concatenates the sequences. However, as I noted in a comment above, there are performance costs to deeply nested concats. You might consider also using a SelectMany to flatten a bunch of iterators:

var carLists = new[]{GetRedCars(), GetGreenCars(), GetBlueCars()};
var allCars = from carList in carLists
              from car in carList 
              select car;
foreach(var c in allCars) { ... }

You might find that to be more flexible should you discover that you actually have way more than three lists to iterate.

Eric Lippert