Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a 2D Array of 2D Arrays into a single 2D Array

So I have an object called, for lack of a better word, MatricesMatrix which is a Matrix of Matrices (everything is stored as a double[,]). I want to strip all of the values from the inner matrices into one big Matrix. Here is what I have so far:

public Matrix ConvertToMatrix()
    {
        //Figure out how big the return matrix should be
        int totalRows = this.TotalRows();
        int totalColumns = this.TotalColumns();

        Matrix returnMatrix = new Matrix(totalRows, totalColumns);

        List<object> rowElementsList = new List<object>();

        //"outer" index means an index of the MatricesMatrix
        //"inner" index means an index of a Matrix within the Matrices Matrix

        //outer row loop
        for (int outerRowIndex = 0; outerRowIndex < NumberOfRows; outerRowIndex++)
        {
            //outer column loop
            for (int outerColumnIndex = 0; outerColumnIndex < NumberOfColumns; outerColumnIndex++)
            {
                Matrix currentMatrix = GetElement(outerRowIndex, outerColumnIndex);
                object element = null;

                //inner row loop
                for (int innerRowIndex = 0; innerRowIndex < currentMatrix.NumberOfRows; innerRowIndex++)
                {
                    //inner column loop
                    for (int innerColumnIndex = 0; innerColumnIndex < currentMatrix.NumberOfColumns; innerColumnIndex++)
                    {
                       element = currentMatrix.GetElement(innerRowIndex, innerColumnIndex);                          
                    }            
                }

                returnMatrix.SetElement(outerRowIndex, outerColumnIndex, (double)element);      





            }
        }

        return returnMatrix;
    }

Note that I have determined programmatically the total number of rows and columns the returnMatrix needs to have.

Here are some more guidelines and output cases:

  • Each element of the big matrix should be in the same position relative to the other elements of the big matrix that came from the Matrix inside of MatricesMatrix that the element came from.
  • Each "matrix" (no longer in matrix form) inside of the big matrix should be in the same position relative to the other matrices inside of the big matrix as it was inside of the MatricesMatrix (with no overlapping, and 0's in any spaces left empty).

CASE 1

Given this input: a MatricesMatrix(2,2) with [0,0] = (2x2 matrix), [0,1] = (2x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (2x3 matrix). That is,

MatricesMatrix Input

Output must be:

Output Case 2

CASE 2

Given this input: a MatricesMatrix(2,2) with [0,0] = (1x1 matrix), [0,1] = (3x3 matrix), [1,0] = (2x2 matrix), and [1,1] = (4x4 matrix). That is,

Case 2 Input

Output should be something like:

Case 2 Output

Any assistance would be greatly appreciated!

UPDATE: Here is a unit test for Case 1 that should pass:

    [TestMethod]
    public void MatricesMatrix_ConvertToMatrixTest()
    {
        Matrix m1 = new Matrix(2);
        Matrix m2 = new Matrix(2, 3);
        Matrix m3 = new Matrix(2);
        Matrix m4 = new Matrix(2, 3);

        double[] m1Row1 = { 1, 1 };
        double[] m1Row2 = { 1, 1 };

        double[] m2Row1 = { 2, 2, 2 };
        double[] m2Row2 = { 2, 2, 2 };            

        double[] m3Row1 = { 3, 3 };
        double[] m3Row2 = { 3, 3 };

        double[] m4Row1 = { 4, 4, 4 };
        double[] m4Row2 = { 4, 4, 4 };

        m1.SetRowOfMatrix(0, m1Row1);
        m1.SetRowOfMatrix(1, m1Row2);
        m2.SetRowOfMatrix(0, m2Row1);
        m2.SetRowOfMatrix(1, m2Row2); 
        m3.SetRowOfMatrix(0, m3Row1);
        m3.SetRowOfMatrix(1, m3Row2);
        m4.SetRowOfMatrix(0, m4Row1);
        m4.SetRowOfMatrix(1, m4Row2);

        MatricesMatrix testMatricesMatrix = new MatricesMatrix(2, 2);

        testMatricesMatrix.SetElement(0, 0, m1);
        testMatricesMatrix.SetElement(0, 1, m2);
        testMatricesMatrix.SetElement(1, 0, m3);
        testMatricesMatrix.SetElement(1, 1, m4);

        Matrix expectedResult = new Matrix(4, 5);

        double[] expectedRow1 = { 1, 1, 2, 2, 2 };
        double[] expectedRow2 = { 1, 1, 2, 2, 2 };
        double[] expectedRow3 = { 3, 3, 4, 4, 4 };
        double[] expectedRow4 = { 3, 3, 4, 4, 4 };

        expectedResult.SetRowOfMatrix(0, expectedRow1);
        expectedResult.SetRowOfMatrix(1, expectedRow2);
        expectedResult.SetRowOfMatrix(2, expectedRow3);
        expectedResult.SetRowOfMatrix(3, expectedRow4);

        Matrix actualResult = testMatricesMatrix.ConvertToMatrix();

        (actualResult == expectedResult).Should().BeTrue();

    }
like image 496
Matt VS Avatar asked Jul 10 '14 13:07

Matt VS


1 Answers

I started with a simple Matrix class to hold the double[,]s. Nothing too fancy, just a simple array-of-arrays with a row and column count and array accessor.

class Matrix<T>
{
    public int Rows { get; private set; }
    public int Cols { get; private set; }

    private T[,] mat;

    public Matrix(int rowCount, int colCount)
    {
        Rows = rowCount;
        Cols = colCount;
        mat = new T[Rows, Cols];
    }

    public T this[int r, int c]
    {
        get { return mat[r, c]; }
        set { mat[r, c] = value; }
    }
}

Your second case looks more difficult (and like a better test of correctness) than the first, so I set up a metamatrix to match that.

public static Matrix<double[,]> BuildMetaMatrix()
{
    Matrix<double[,]> m = new Matrix<double[,]>(2, 2);

    m[0, 0] = new double[,]
    {
        { 1 }
    };

    m[0, 1] = new double[,]
    {
        { 3, 3, 3 },
        { 3, 3, 3 },
        { 3, 3, 3 }
    };

    m[1, 0] = new double[,]
    {
        { 2, 2 },
        { 2, 2 }
    };

    m[1, 1] = new double[,]
    {
        {4, 4, 4, 4},
        {4, 4, 4, 4},
        {4, 4, 4, 4},
        {4, 4, 4, 4}
    };

    return m;
}

For convenience, I made a Place function that puts one matrix into another one at the given location.

static void Place(double[,] src, double[,] dest, int destR, int destC)
{
    for (int row = 0; row < src.GetLength(ROW_DIM); row++)
    {
        for (int col = 0; col < src.GetLength(COL_DIM); col++)
        {
            dest[row + destR, col + destC] = src[row, col];
        }
    }
}

The magic numbers fed into GetLength() were just asking for mistakes, so I defined some constants for them (ROW_DIM = 0 and COL_DIM = 1). I decided to handle the padding by figuring out how wide a column is and how tall a row is and skipping any extra elements after Place()ing the sub-matrix in. A GetRowHeight() and GetColWidth() method figure out the values.

public static int GetRowHeight(Matrix<double[,]> m, int row)
{
    int maxSeen = 0;

    for (int col = 0; col < m.Cols; col++)
    {
        if (m[row, col].GetLength(ROW_DIM) > maxSeen)
        {
            maxSeen = m[row, col].GetLength(ROW_DIM);
        }
    }

    return maxSeen;
}

public static int GetColWidth(Matrix<double[,]> m, int col)
{
    int maxSeen = 0;

    for (int row = 0; row < m.Rows; row++)
    {
        if (m[row, col].GetLength(COL_DIM) > maxSeen)
        {
            maxSeen = m[row, col].GetLength(COL_DIM);
        }
    }

    return maxSeen;
}

A Flatten() function loops through all the sub-matrices, Place()ing them at the appropriate row and column in a new matrix. It updates the next row and column after each Place() using the GetRowHeight() and GetColWidth() functions.

Matrix<double> Flatten(Matrix<Matrix<double>> src)
{
    // (7, 6) == (this.TotalRows(), this.TotalColumns())
    // from your code.
    Matrix<double> dest = new Matrix<double>(7, 6);

    int nextRow = 0;
    int nextCol = 0;

    for (int row = 0; row < src.Rows; row++)
    {
        for (int col = 0; col < src.Rows; col++)
        {
            dest.Place(src[row, col], nextRow, nextCol);
            nextCol += GetColWidth(src, col);
        }
        nextRow += GetRowHeight(src, row);
        nextCol = 0;
    }

    return dest;
}

A little glue to test it out...

static void Main(string[] args)
{
    Matrix<double[,]> src = BuildMetaMatrix();
    double[,] dest = Flatten(src);

    Print(dest);
    Console.ReadLine();
}

static void Print(double[,] matrix)
{
    for (int row = 0; row < matrix.GetLength(ROW_DIM); row++)
    {
        for (int col = 0; col < matrix.GetLength(COL_DIM); col++)
        {
            Console.Write(matrix[row, col] + "\t");
        }
        Console.Write("\n");
    }
}

...and you get an output just like your second case with all the oddly fitting matrices and 0s in the empty places.*

1       0       3       3       3       0
0       0       3       3       3       0
0       0       3       3       3       0
2       2       4       4       4       4
2       2       4       4       4       4
0       0       4       4       4       4
0       0       4       4       4       4

*The destination matrix gets its values initialized to default(double), which happens to be 0 (the value you wanted). If you need something other than default(double) for the empty places, you can probably get them by iterating over the new matrix and writing the new default value everywhere before Flatten()ing the metamatrix.

(Thanks to Jeff Mercado for pointing out that multidimensional arrays' GetLength() method can be used to find their dimensions.)

like image 117
Mirinth Avatar answered Sep 30 '22 23:09

Mirinth