3

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.

Community
  • 1
  • 1
david.pfx
  • 10,520
  • 3
  • 30
  • 63
  • 1
    I'm not sure what your goal is. If the answer of the last link you posted works then why not simply convert it into an extension method so you can use it like `.Where(...).Select(...).ToRectangularArray()`? Or do you want `from x in xs select x.y` to return a multidimensional array instead of an `IEnumerable`? – Dirk Oct 07 '14 at 09:54
  • Linq works with IEnumerables. All its return types are some flavour of IEnumerable. If you want to return a multidimensional array then I would imagine you'd have to write an equivalent of `Enumerable.ToArray()` which doesn't currently exist. – Chris Oct 07 '14 at 10:24

2 Answers2

5

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)

Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78
  • 1
    This has got to be worth a +1 for sheer ingenuity, but it really isn't what I had in mind. See edit. – david.pfx Oct 07 '14 at 10:50
  • 1
    I understand what you feel. There's literally 1000 things missing from .NET framework that should have been there. The good thing is; if you're smart, you now NEVER have to face this issue again, because you added this to your own "mini-experience-library", the bad thing is: others have to face this issue. Help and grow the community, you can write the method & unit tests, and ask Jon to pull it into MoreLINQ :-). As for your "real question", the answer is no, as you said. – Erti-Chris Eelmaa Oct 07 '14 at 10:57
  • Good comment. I'm not really in that community, but hopefully someone else might pick it up and run with it. – david.pfx Oct 08 '14 at 00:38
1

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).

ken2k
  • 48,145
  • 10
  • 116
  • 176