简体   繁体   中英

Jagged to Multidimensional Array

A co-worker and I are currently engaged in a series of coding challenges.

However we like to make things slightly more interesting and golf our answers down (Yes I know, C# is not the greatest golfing language)

Our most recent one involved rotating a cube (int[,]). So eventually we came up with a bet, if I can get this in one line he buys me lunch and vice versa.

After many backspaces and words my mother would hate to hear me say, I finally got it, or so I thought.

I ended up with int[][].

And as far as I can see there is no easy way to convert this to int[,]. so I'm asking if it is actually possible within one line.

EDIT 1

I would post my source code, but then my co-worker could find it and I could lose a free lunch.

You'll need at least two lines, one for declaring your result array and one for the actual copying of the data.

You could first flatten your array of array to a single array, and the use Buffer.BlockCopy to copy all data to the result array.

Here's an example:

var source = new int[][] {
    new int[4]{1,2,3,4},
    new int[4]{5,6,7,8},
    new int[4]{1,3,2,1},
    new int[4]{5,4,3,2}
};

var expected = new int[4,4] {
    {1,2,3,4},
    {5,6,7,8},
    {1,3,2,1},
    {5,4,3,2}
};

var result = new int[4, 4];
// count = source.Length * source[0].Length * sizeof(int) = 64, since BlockCopy is byte based
// to be dynamically you could also use System.Runtime.InteropServices.Marshal.SizeOf(source[0][0]) instead of sizeof(int)
Buffer.BlockCopy(source.SelectMany(r => r).ToArray(), 0, result, 0, 64);

result.Dump("result");
expected.Dump("expected");

Result:

在此输入图像描述

If you insist of being fancy: you could call BlockCopy dynamically with an delegate to have that delegate return Object so you can use it for an assignment of an anonymous class, which is apparently in the spirit of your rules, and wrap everything into a collection so you end with a single line monster like this:

var result = new[]{ new int[4, 4] }.Select(x => new { r = x, tmp = Delegate.CreateDelegate(typeof(Action<Array, int, Array, int, int>), typeof(Buffer).GetMethod("BlockCopy")).DynamicInvoke(new Object[]{source.SelectMany(r => r).ToArray(), 0, x, 0, 64})}).First().r;

You should try learning javascript (the one I'm using is a very common pattern in libraries).

int[][] jagged = new[] { new[] { 1, 2, 3, 4, 5 }, new[] { 6, 7, 8, 9, 10 } };

int[,] array = ((Func<int[][], int[,]>)(x =>
{
    int[,] temp = new int[x.Length, x.Length != 0 ? x[0].Length : 0];
    for (int i = 0; i < x.Length; i++)
    {
        for (int j = 0; j < x[0].Length; j++)
        {
            temp[i, j] = x[i][j];
        }
    }

    return temp;
}))(jagged);

The int[,] array = can be made a single line just removing the end of lines. I declare an anonymous method and then call it (see the (jagged) ? It is me calling the anonymous method).

single statement version

int[,] array = jagged.Length == 0 ? 
    new int[0,0] : 
    jagged.SelectMany(x => x)
        .Select((x, ix) => new
        {
            i = ix / jagged[0].Length,
            y = ix % jagged[0].Length,
            val = x
        })
        .Aggregate(new int[jagged.Length, jagged[0].Length], (md, x) => (md[x.i, x.y] = x.val) == x.val ? md : null);

Here I'm using the Aggregate() method. The TSeed is the destination multi-dimensional array. Note the (md[xi, xy] = x.val) == x.val ? md : null (md[xi, xy] = x.val) == x.val ? md : null : I need to assign the md[xi, xy] = x.val BUT I need to return md (because Aggregate requires the function to work this way). I make an useless check (md[xi, xy] = x.val) == x.val and use the ternary operator to return md .

Note that I'm closing a variable ( jagged ), but the closure could be removed (I think).... Mmmmh seems to be complex.

If you are allowed to use Func<> and lambda you can certainly do it and make a generic extension to transform the object you are calling it on.

/// <typeparam name="T">Output type</typeparam>
/// <typeparam name="U">Calling type</typeparam>
/// <param name="obj">object to pipe</param>
/// <param name="func">blackbox function</param>
/// <returns>whatever</returns>
public static T ForThis<T,U> (this U obj, Func<U,T> func)
{
    return func(obj);
}

With this you should be able to transform int[,] to int[][] by doing something like:

int[][] output = input.ForThis<int[][], int[,]>((obj) =>
{
    // transform obj == input of type int[,] into int[][]
    throw new NotImplementedException();
});

Although I admit that this solution really feels like cheating because you are just sort of wrapping the multiline transformation into a lambda.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM