C# flattening/expanding a 3D matrix into a jagged

2019-08-07 02:49发布

问题:

I have to flatted a 3d array in order to be serialized. Let's start with this:

int[,,] array3D = new int[,,] { 
            { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
            { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
            { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
        };

which makes it like this (something like 1,2,3,4,...,24):

So now I have this s/r

public static T[] Flatten<T>(T[,,] arr)
{
    int rows0 = arr.GetLength(0);
    int rows1 = arr.GetLength(1);
    int rows2 = arr.GetLength(2);
    T[] arrFlattened = new T[rows0 * rows1* rows2];

    int i, j, k;
    for (k = 0; k < rows2; k++)
    {
        for (j = 0; j < rows1; j++)
        {
            for (i = 0; i < rows0; i++)
            {
                var test = arr[i, j, k];
                int index = i + j * rows0 + k * rows1;
                arrFlattened[index] = test;
            }
        }
    }
    return arrFlattened;
}

which flattens the 3d matrix into a 1d array

I am not smart enough to understand if the procedure is correct but let's go further. I then Expand with the following s/r

public static T[,,] Expand<T>(T[] arr, int rows0, int rows1)
{
    int length = arr.GetLength(0);
    int rows2 = length / rows0 / rows1;

    T[,,] arrExpanded = new T[rows0, rows1, rows2];
    for (int k = 0; k < rows2; k++)
    {
        for (int j = 0; j < rows1; j++)
        {
            for (int i = 0; i < rows0; i++)
            {
                T test = arr[i + j * rows0 + k * rows1];
                arrExpanded[i, j, k] = test;
            }
        }
    }
    return arrExpanded;
}

but the result is the following:

So nothing like 1,2,3,4,5....24 I know that the error might be a trivial one but try as I might I can't find it. Thank in advance.

Patrick


Thanks for helping all 3 solution were amazing and working but I chose the one more easily to understand and debug for me

回答1:

I am assuming that you want to know what is the mistake in your code more than you want to know the quickest way to get to the answer. Your index calculation is wrong. You are calculating it like this:

int index = i + j * rows0 + k * rows1;

But you actually need to multiply the last term not just by rows1 but by rows0 too:

int index = i + j * rows0 + k * rows1 * rows0;

Also, it makes sense to swap the order of dimensions that are iterated in for loops to get results in order. The final code for that would be:

    public static T[] Flatten<T>(T[,,] arr)
    {
        int rows0 = arr.GetLength(0);
        int rows1 = arr.GetLength(1);
        int rows2 = arr.GetLength(2);
        T[] arrFlattened = new T[rows0 * rows1* rows2];

        int i, j, k;
        for (k = 0; k < rows0; k++)
        {
            for (j = 0; j < rows1; j++)
            {
                for (i = 0; i < rows2; i++)
                {
                    var test = arr[k, j, i];
                    int index = i + j * rows2 + k * rows1 * rows2;
                    arrFlattened[index] = test;
                }
            }
        }
        return arrFlattened;
    }

    public static T[,,] Expand<T>(T[] arr, int rows0, int rows1)
    {
        int length = arr.GetLength(0);
        int rows2 = length / rows0 / rows1;

        T[,,] arrExpanded = new T[rows0, rows1, rows2];
        int i, j, k;
        for (k = 0; k < rows0; k++)
        {
            for (j = 0; j < rows1; j++)
            {
                for (i = 0; i < rows2; i++)
                {
                    T test = arr[i + j * rows2 + k * rows1 * rows2];
                    arrExpanded[k, j, i] = test;
                }
            }
        }
        return arrExpanded;
    }


回答2:

You could give this a go:

void Main()
{
    int[,,] array3D = new int[,,]
    {
        { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
        { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
        { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
    };

    var flattened = array3D.Cast<int>().ToArray();

    var restored = Expand(flattened, 3, 4);
}

public static T[,,] Expand<T>(T[] arr, int rows0, int rows1)
{
    int length = arr.GetLength(0);
    int rows2 = length / rows0 / rows1;
    int x = 0;

    T[,,] arrExpanded = new T[rows0, rows1, rows2];
    for (int i = 0; i < rows0; i++)
    {
        for (int j = 0; j < rows1; j++)
        {
            for (int k = 0; k < rows2; k++)
            {
                T test = arr[x++];
                arrExpanded[i, j, k] = test;
            }
        }
    }
    return arrExpanded;
}

It worked fine for me.



回答3:

With a help of Linq OfType<T>(), it's easy to have int[] from any multidimensional array:

var result = source.OfType<int>().ToArray();

Demo:

using System.Linq;

...

int[,,] array3D = new int[,,] {
  { {  1,  2}, {  3,  4}, {  5,  6}, { 7,  8 } },
  { {  9, 10}, { 11, 12}, { 13, 14}, {15, 16 } },
  { { 17, 18}, { 19, 20}, { 21, 22}, {23, 24 } },
};

var result = array3D.OfType<int>().ToArray();

Console.Write(string.Join(", ", result)); 

Outcome:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24

We can use modulo arithmetics to Expand array back:

private static T[,,] Expand<T>(T[] value, int length1, int length2, int length3) {
  T[,,] result = new T[length1, length2, length3];

  for (int i = 0; i < value.Length; ++i) {
    int r = i / (length3 * length2);
    int c = i /  length3 % length2;
    int h = i %  length3;

    result[r, c, h] = value[i];
  }

  return result;
}

E.g.

int[,,] back = Expand(result, 3, 4, 2);


回答4:

To flatten a multidimensional array just use Cast<T>().ToArray().

var d3 = new int[,,]
{
    { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
    { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
    { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
};
var d1 = d3.Cast<int>().ToArray();
Console.WriteLine(string.Join(" ", d1));

Gives:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

To expand use this:

static int[,,] Expand(int[] array, int size2, int size3)
{
    var size = new[] { array.Length / size2 / size3, size2, size3 };
    var res = Array.CreateInstance(typeof(int), size);
    for (var i = 0; i < array.Length; i++)
        res.SetValue(array[i], GetMultidimensionalIndex(i, size));

    return (int[,,])res;
}

static int[] GetMultidimensionalIndex(int index, int[] size)
{
    var factors = size.Select((item, i) => size.Skip(i).Aggregate((a, b) => a * b)).ToArray();
    var factorsHelper = factors.Zip(factors.Skip(1).Append(1), (Current, Next) => new { Current, Next }).ToArray();
    return factorsHelper.Select(item => index % item.Current / item.Next).ToArray();
}

Usage:

var d3 = new int[,,]
{
    { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
    { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
    { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
};
Console.WriteLine("Original:");
Print3DArray(d3);

var flat = d3.Cast<int>().ToArray();
Console.WriteLine("Flat:");
Console.WriteLine(string.Join(" ", flat));

var expanded = Expand(flat, d3.GetLength(1), d3.GetLength(2));
Console.WriteLine("Expanded:");
Print3DArray(expanded);

with helper method:

static void Print3DArray(int[,,] array)
{
    Console.WriteLine("{");
    for (int i = 0; i < array.GetLength(0); i++)
    {
        Console.Write("    {");
        for (int j = 0; j < array.GetLength(1); j++)
        {
            Console.Write(" {");
            for (int k = 0; k < array.GetLength(2); k++)
            {
                Console.Write($" {array[i, j, k]}");
            }
            Console.Write(" }");
        }
        Console.WriteLine(" }");
    }
    Console.WriteLine("}");
}

Gives:

Original:
{
    { { 1 2 } { 3 4 } { 5 6 } { 7 8 } }
    { { 9 10 } { 11 12 } { 13 14 } { 15 16 } }
    { { 17 18 } { 19 20 } { 21 22 } { 23 24 } }
}
Flat:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Expanded:
{
    { { 1 2 } { 3 4 } { 5 6 } { 7 8 } }
    { { 9 10 } { 11 12 } { 13 14 } { 15 16 } }
    { { 17 18 } { 19 20 } { 21 22 } { 23 24 } }
}