6

For example, if I had two lists, I'd do:

foreach (Item item1 in lists[0])
  foreach (Item item2 in lists[1])
    // Do something with item1 and item2

Or if I had three, I'd do

foreach (Item item1 in lists[0])
  foreach (Item item2 in lists[1])
    foreach (Item item3 in lists[2])
      // Do something with item1, item2, and item3

but if I don't know at compile time how many lists are in the lists collection, how can I easily iterate over every permutation?

A C# solution is ideal, but a solution in any language that demonstrates a suitable algorithm would be handy.

A good 2-dimensional example would be a list of columns and a list of rows on a spreadsheet, where I need to do processing on each cell. It's an n-dimensional problem, however.

Flynn1179
  • 11,925
  • 6
  • 38
  • 74

3 Answers3

5

There is a wonderful article on the subject by Eric Lippert.

I highly suggest reading the article, as it describes the process by which you can arrive at the result, but at the end the resulting code is short and sweet:

(Copied verbatim from the link)

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})); 
}
Servy
  • 202,030
  • 26
  • 332
  • 449
1
    public static IEnumerable<T[]> IterateOverLists<T>(this IList<IEnumerable<T>> lists )
    {
        var array = new T[lists.Count];
        return IterateOverLists( lists, array, 0 );
    }
    private static IEnumerable<T[]> IterateOverLists<T>(this IList<IEnumerable<T>> lists, T[] array, int index)
    {
        foreach (var value in lists[index])
        {
            array[index] = value;
            if (index == lists.Count - 1)
            {
                // can make a copy of the array here too...
                yield return array;
            }
            else
            {
                foreach (var item in IterateOverLists(lists, array, index + 1))
                {
                    yield return item;
                }
            }
        }
    }

If one of your lists is empty it will kill the whole thing, but you should be able to work around that...

James Curtis
  • 784
  • 4
  • 15
  • @Servy - do you have any evidence of / explanation for that statement? – James Curtis Oct 10 '12 at 16:59
  • I'm looking for the article on the subject. In the meantime, you need to change `if (index == lists.Count)` to use `Count - 1` or you'll just get index out of bounds errors. – Servy Oct 10 '12 at 17:04
  • [Found it](http://blogs.msdn.com/b/toub/archive/2004/10/29/249858.aspx). Note it's a 3 part series; this is just the first part. – Servy Oct 10 '12 at 17:08
  • @Servy - The issue mentioned here is one of recursive iteration, not recursive iterator blocks. The code snipped provided by Eric Lippert, while shorter, still utilizes recursive iteration. – James Curtis Oct 10 '12 at 17:53
0
for (int i = 0; i < lists.Length; i++) {
    foreach (Item item in lists[i]) {
       ....
    }
}
001
  • 13,291
  • 5
  • 35
  • 66
  • 1
    That will only cover a 2-D case (which he explicitly calls out). He want a more general solution for any n-diminsional set of lists. – shenles Oct 10 '12 at 16:41