How to get all possible combinations for n arrays with different number of elements?
This works:
Func< IEnumerable<IEnumerable<int>>, IEnumerable<IEnumerable<int>>> f0 = null;f0 = xss =>{ if (!xss.Any()) { return new [] { Enumerable.Empty<int>() }; } else { var query = from x in xss.First() from y in f0(xss.Skip(1)) select new [] { x }.Concat(y); return query; }};Func<IEnumerable<IEnumerable<int>>, IEnumerable<string>> f = xss => f0(xss).Select(xs => String.Join(",", xs));
So if I have this input:
var input = new []{ new [] { 1, 2, 3, 4, }, new [] { 6, 7, 5, 2, 1, }, new [] { 22, 4, 6, 8, 4, 8, 5, 4, },};
I can get the results this way:
var results = f(input);
Here's a version which simply sums the results, as per the request in the comments:
Func<IEnumerable<IEnumerable<int>>, IEnumerable<int>> f = null;f = xss =>{ if (!xss.Any()) { return new [] { 0 }; } else { var query = from x in xss.First() from y in f(xss.Skip(1)) select x + y; return query; }};var input = new []{ new [] { 1, 2, 3, 4, }, new [] { 6, 7, 5, 2, 1, }, new [] { 22, 4, 6, 8, 4, 8, 5, 4, },};var results = f(input);
I think the linq version looks awesome:
from i in afrom j in bfrom k in cfrom m in dselect String.Join(",", i, j, k, m)
But the answer to your question is not easy.Eric Lippert wrote about it on his blog:
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx
The following extension method imitates a nested stack of foreach
loops:
public static class Ext{ public static void ForEachNested<T>( this IEnumerable<IEnumerable<T>> sources, Action<IEnumerable<T>> action) { var enumerables = sources.ToArray(); Stack<IEnumerator<T>> fe = new Stack<IEnumerator<T>>(); fe.Push(enumerables[0].GetEnumerator()); while (fe.Count > 0) { if (fe.Peek().MoveNext()) { if (fe.Count == enumerables.Length) action(new Stack<T>(fe.Select(e => e.Current))); else fe.Push(enumerables[fe.Count].GetEnumerator()); } else { fe.Pop().Dispose(); } } }}
You could use it as follows:
new[] { a, b, c, d }.ForEachNested(e => { Response[f++] = string.Join(",", e); });
or, to do your maths,
new[] { a, b, c, d }.ForEachNested(e => { Response[f++] = e.Sum(); });
This has the advantage of not performing hundreds of array allocations.
Ordinarily I'd shy away from using a LINQ-like method to act rather than query but as this is closely imitating how a foreach
loop works I don't mind so much.