david.pfx
david.pfx

Reputation: 10868

Can LINQ construct and return a multidimensional array

Yes, I do know the difference between a jagged array [][] and a multidimensional array [,].

Yes, I have looked for duplicates and found these:

The last one of these contains a function to do the job. But my question is still: is there any way (no matter how contorted or contrived) to persuade LINQ to construct and return a result in the form of a multidimensional array (and not a jagged array)?


It's not really relevant to the question, but the problem that led here was one of those little jobs that looked like it was "made for LINQ", except that the function I needed to call had been defined with a multidimensional array parameter. The LINQ code to produce the answer as a jagged array took about 5 minutes to write, and then what? Did I miss something, or is there really no way for LINQ to do the job?


I supposed I should say: other than writing a chunk of code to do it the old way and embedding it as a lambda or calling it as an extension method. For now it does look as if LINQ can neither produce nor consume multidimensional arrays, and the answer to my question is no.

Upvotes: 3

Views: 2171

Answers (2)

ken2k
ken2k

Reputation: 48985

I supposed I should say: other than writing a chunk of code to do it the old way and embedding it as a lambda or calling it as an extension method. For now it does look as if LINQ can neither produce nor consume multidimensional arrays, and the answer to my question is no.

It is no. Most LINQ methods are extension methods of IEnumerable<T>. While a jagged array (say, int[int[]]) implements IEnumerable<int[]>, a multidimensional array doesn't implement IEnumerable<T>, so you can't use any LINQ extension method.

The only thing you could do is to eventually return a multidimensional array using a projection (Select).

Upvotes: 1

Erti-Chris Eelmaa
Erti-Chris Eelmaa

Reputation: 26268

Uhm.. well.. sure? This is an example how to turn list of integers into 2 dimensional array if possible.

void Main()
{
    var wat = new[]{2, 4, 5, 6, 7, 8}
        .Select(x => new { dummy=1, val= x})
        .GroupBy(x => x.dummy) // group everything into one, "UNFLATTEN"
        .Select(gr => 
            {
                var elements = gr.Select(x => x.val).ToList();
                if(elements.Count == 0 || elements.Count % 2 != 0)
                    throw new NotSupportedException("I don't know what to do.");

                var halfSize = elements.Count / 2;

                int[,] ret = new int[2, halfSize];

                for(var k = 0; k < halfSize; k++){
                    ret[0, k] = elements[k];
                    ret[1, k] = elements[elements.Count - halfSize + k];
                }

                return ret;
            }
        ).First();

    wat.Dump();
}

Output:

_ 0 1 2

0 2 4 5

1 6 7 8

As others have said, you're best off by creating new extension method:

static class LinqExtensions
{
    public static T[,] ToRectangularArray<T>(this IEnumerable<IEnumerable<T>> arrays)
    {
        // TODO: Validation and special-casing for arrays.Count == 0
        // TODO: Rename "arrays"
        // TODO: Make sure that Count() is used only once,
        // TODO: ElementAt() does not work everywhere, you're better off using ToList() before!
        int minorLength = arrays.First().Count();
        T[,] ret = new T[arrays.Count(), minorLength];
        for (int i = 0; i < arrays.Count(); i++)
        {
            var array = arrays.ElementAt(i);
            if (array.Count() != minorLength)
            {
                throw new ArgumentException
                    ("All arrays must be the same length");
            }
            for (int j = 0; j < minorLength; j++)
            {
                ret[i, j] = array.ElementAt(j);
            }
        }
        return ret;
    }
}

void Main()
{
    var wat = new[]{new[]{2, 4, 5}, new[]{6, 7, 8}}.ToRectangularArray();
    wat.Dump();
}

for convienence, you might want to add the same extension method for IEnumerable<T>, and call it as such: ToRectangularArray<T>(this IEnumerable<T> input, int dimensions = 2)

Upvotes: 5

Related Questions