Possible Duplicate:
Generating all Possible Combinations
Is there a good LINQ way to do a cartesian product?
How to generate combination of N elements with limited supply of 2 each without explicit nested loops
I have a list of lists, and I want to iterate all the possible combinations where I choose one element from each inner list. This is pretty straightforward if I know at compile-time how many lists there are, but how can I do it when I don't know in advance how many lists there will be?
If I have three lists (and if I know, at compile-time, that there will be exactly three lists), and I want all the combinations of choosing a single element from each of the three lists, I can do that easily with a LINQ query:
var list1 = new[] { 1, 2 };
var list2 = new[] { 3, 4 };
var list3 = new[] { 5, 6 };
var combinations = from item1 in list1
from item2 in list2
from item3 in list3
select new[] { item1, item2, item3 };
// Results:
// {1, 3, 5}
// {1, 3, 6}
// {1, 4, 5}
// {1, 4, 6}
// {2, 3, 5}
// {2, 3, 6}
// {2, 4, 5}
// {2, 4, 6}
But how can I do the same thing when I don't know at compile-time how many lists there will be?
var lists = new[] {
new[] { 1, 2 },
new[] { 3, 4 },
new[] { 5, 6 } };
var combinations = ???;
// This particular example happens to be the same inputs as above, so it
// has the same expected outputs. But there could be two lists instead,
// or four, so the three hard-coded "from" clauses won't work.
It seems like this should actually be doable in LINQ -- SelectMany already does the equivalent of two nested foreach loops, so all I need to do is do a bunch of SelectMany calls and then combine all the results with another SelectMany. Or something. But when it starts getting meta like that, my brain gets all tied in knots. I can't get a handle on how to put the pieces together. I can't even figure out what the generic type arguments to the outer SelectMany call would be.
How can I iterate those lists of lists, and return all the combinations, without knowing at compile-time how many lists there will be?
(Note: everywhere I used arrays above, I'd be fine with using IEnumerable<T>
instead. Arrays are easier to write in sample code, but I'm expecting that the output is more likely to be in the form IEnumerable<IEnumerable<int>>
rather than the int[][]
I show in my sample output above.)
You don't use SelectMany to combine the SelectMany calls; you use Aggregate. Code courtesy of Eric Lippert (answering on a question that's much more specific than this one, but giving a general answer that fits this question as well):
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}) :
);
}
As with all of Eric's answers, he includes a detailed discussion that lays out exactly how and why this works, in terms of the equivalent non-LINQ code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With