Igorek Igorek - 1 year ago 99
C# Question

Dynamically cross-join multiple different-size collections together in Linq (C#)

I have an unknown number of buckets(collections), and each bucket having an unknown number of entities

I need to produce a cartesian product of all the entities, so that I endup with a single COLLECTION that has ARRAYS of entities and in each array, there is 1 representetive from EVERY bucket.

So that if I have 5 buckets (B1..B5), and buckets B1, B2 have 1 item each, and bucket B3, B4 and B5 have 4, 8 and 10 items each, I'll have a collection of 320 arrays, and each array will have 5 items.

The only stupud issue here, is that both size of buckets and number of buckets is unknown at development time.

Performance is not super important here, as most of the time, my buckets will have only 1 entity, and only rarely will there be times when some of my buckets will contain 20-30 items...and I'll usually have 5-30 buckets

I'd love to utilize linq here in someway, but my brain is getting fried as I try to imagine how this would work

dbc dbc
Answer Source

You could create an extension method like the following:

public static class EnumerableExtensions
    public static IEnumerable<TValue []> Permutations<TKey, TValue>(this IEnumerable<TKey> keys, Func<TKey, IEnumerable<TValue>> selector)
        var keyArray = keys.ToArray();
        if (keyArray.Length < 1)
            yield break;
        TValue [] values = new TValue[keyArray.Length];
        foreach (var array in Permutations(keyArray, 0, selector, values))
            yield return array;

    static IEnumerable<TValue []> Permutations<TKey, TValue>(TKey [] keys, int index, Func<TKey, IEnumerable<TValue>> selector, TValue [] values)
        Debug.Assert(keys.Length == values.Length);

        var key = keys[index];
        foreach (var value in selector(key))
            values[index] = value;
            if (index < keys.Length - 1)
                foreach (var array in Permutations(keys, index+1, selector, values))
                    yield return array;
                yield return values.ToArray(); // Clone the array;

As an example, it could be used like:

    public static void TestPermutations()
        int [][] seqence = new int [][]
            new int [] {1, 2, 3},
            new int [] {101},
            new int [] {201},
            new int [] {301, 302, 303},

        foreach (var array in seqence.Permutations(a => a))
            Debug.WriteLine(array.Aggregate(new StringBuilder(), (sb, i) => { if (sb.Length > 0) sb.Append(","); sb.Append(i); return sb; }));

and produce the following output:


Is that what you want?