Convert 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 matrices (everything is stored as double[,] ). I want to remove all values โ€‹โ€‹from internal matrices into one large matrix. Here is what I still have:

 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 programmatically determined the total number of rows and columns that returnMatrix should have.

The following are some guidelines and examples of output:

  • Each element of the large matrix must be in the same position relative to the other elements of the large matrix that came from the matrix inside the MatricesMatrix matrix from which the element came.
  • Each "matrix" (no longer in matrix form) inside the large matrix should be in the same position relative to other matrices inside the large matrix, as it was inside the Matrices matrix (without overlapping and 0 in any spaces remain 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) . I.e

MatricesMatrix Input

The conclusion should 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) . I.e

Case 2 input

The output should look something like this:

Case 2 output

Any help would be greatly appreciated!

UPDATE: Here is the unit test for case 1, which 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(); } 
+8
arrays c # matrix multidimensional-array
source share
3 answers

I started with a simple Matrix class to hold double[,] s. Nothing too unusual, just a simple array of arrays with row and column counting and an accessory for the array.

 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; } } } 

The second case looks more complicated (and as a better test of correctness) than the first, so I set the metamatrix to fit this.

 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 created the Place function, which places one matrix in another at a 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 introduced in GetLength() simply requested errors, so I defined some constants for them ( ROW_DIM = 0 and COL_DIM = 1 ). I decided to process the filling by finding out how wide the column is and how high the row is, and skip any additional elements after Place() in the submatrix. Methods A GetRowHeight() and GetColWidth() determine 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; } 

The A Flatten() function goes through all the submatrices, Place() them in the corresponding row and column in the 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 ...

 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 will get the result in the same way as your second case with all the strange matrices and 0 in 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 for empty spaces you need something other than default(double) , you can probably get them, iterate over the new matrix and write the new default value everywhere before Flatten() with the metamatrix.

(Thanks to Jeff Mercado, pointing out that GetLength() multidimensional array GetLength() can be used to determine their sizes.)

+4
source share

I think it would be beneficial for you to break the solution into quadrants that you are trying to fill. All this will be under the assumption that we will combine only 4 matrices in this 2x2 configuration. The same strategies described here can be applied to other sizes of combined matrices.

So, given the 4 matrices A, B, C, and D, we will try to build the resulting matrix in this layout:

 +---+---+ | A | B | +---+---+ | C | D | +---+---+ 

Before we begin, we will need to figure out the size of the final result. This, hopefully, makes sense. We will have the upper half, lower half, left half and right half.

 rows_top = max(rows_A, rows_B) rows_bottom = max(rows_C, rows_D) rows_result = rows_top + rows_bottom cols_left = max(cols_A, cols_C) cols_right = max(cols_B, cols_D) cols_result = cols_left + cols_right 

Then we want to consider which areas of the result matrix we want to copy each of the 4 matrices. Given the beginning in the upper left corner, everything in the right half will be shifted by the size of the left half, everything in the lower half will be shifted by the size of the upper half. The offsets for each of the matrices will be as follows:

 offset_A = (0, 0) offset_B = (0, cols_left) offset_C = (rows_top, 0) offset_D = (rows_top, cols_left) 

Now with all this information, we can begin to create a matrix of results. Just copy the values โ€‹โ€‹from each matrix into the result using offsets.

So, in code, I would do the following:

 // I'm just going to use plain 2D arrays here public T[,] Combine<T>(T[,] a, T[,] b, T[,] c, T[,] d) { // get the total rows var rows_top = Math.Max(a.GetLength(0), b.GetLength(0)); var rows_bottom = Math.Max(c.GetLength(0), d.GetLength(0)); var rows_result = rows_top + rows_bottom; // get the total columns var cols_left = Math.Max(a.GetLength(1), c.GetLength(1)); var cols_right = Math.Max(b.GetLength(1), d.GetLength(1)); var cols_result = cols_left + cols_right; // get the offsets var offset_a = Tuple.Create(0, 0); var offset_b = Tuple.Create(0, cols_left); var offset_c = Tuple.Create(rows_top, 0); var offset_d = Tuple.Create(rows_top, cols_left); // fill 'er up var result = new T[rows_result, cols_result]; Fill(result, a, offset_a); Fill(result, b, offset_b); Fill(result, c, offset_c); Fill(result, d, offset_d); return result; } public void Fill<T>(T[,] result, T[,] source, Tuple<int, int> offset) { for (var i = 0; i < source.GetLength(0); i++) for (var j = 0; j < source.GetLength(1); j++) result[offset.Item1 + i, offset.Item2 + j] = source[i, j]; } 

Then, to demonstrate the result in terms of case 2:

 const string A = "A", B = "B", C = "C", D = "D"; var a = new string[1,1] { { A }, }; var b = new string[3, 3] { { B, B, B }, { B, B, B }, { B, B, B }, }; var c = new string[2, 2] { { C, C }, { C, C }, }; var d = new string[4, 4] { { D, D, D, D }, { D, D, D, D }, { D, D, D, D }, { D, D, D, D }, }; var result = Combine(a, b, c, d); 

This, of course, can be generalized to a matrix of any matrix size. The concept is the same at every stage of the process.

Given mxn matrices, we will try to build the resulting matrix in this layout:

 +-----+-----+-----+ | 0,0 | ... | 0,n | +-----+-----+-----+ | ... | | ... | +-----+-----+-----+ | m,0 | ... | m,n | +-----+-----+-----+ 
  • Get the sizes of each of the fragments.

     rows_0 = max(rows_0_0, ..., rows_0_n) ... rows_m = max(rows_m_0, ..., rows_m_n) rows_result = sum(rows_0, ..., rows_m) cols_0 = max(cols_0_0, ..., cols_m_0) ... cols_n = max(cols_0_n, ..., cols_m_n) cols_result = sum(cols_0, ..., cols_m) 
  • Get offsets for each of the matrices. Each vertical slice is shifted to the left by the total number of columns in the previous vertical slices. Each horizontal slice is shifted down by the total number of rows in previous horizontal slices.

     offset_0_0 = (0, 0) ... offset_m_n = (sum(rows_0, ..., rows_m-1), sum(cols_0, ..., cols_n-1)) 

So now we can create a matrix of results.

 public T[,] Combine<T>(T[,][,] m) { // get the rows var rows = GetSliceRows(m); var rows_result = rows.Sum(); // get the cols var cols = GetSliceCols(m); var cols_result = cols.Sum(); // get the offsets var offsets = GetOffsets(rows, cols); // fill 'er up var result = new T[rows_result, cols_result]; Fill(result, m, offsets); return result; } public int[] GetSliceRows<T>(T[,][,] m) { var sliceRows = new int[m.GetLength(0)]; var segments = m.GetLength(1); for (var i = 0; i < sliceRows.Length; i++) { sliceRows[i] = Enumerable.Range(0, segments) .Select(j => m[i, j].GetLength(0)) .Max(); } return sliceRows; } public int[] GetSliceCols<T>(T[,][,] m) { var sliceCols = new int[m.GetLength(1)]; var segments = m.GetLength(0); for (var j = 0; j < sliceCols.Length; j++) { sliceCols[j] = Enumerable.Range(0, segments) .Select(i => m[i, j].GetLength(1)) .Max(); } return sliceCols; } public Tuple<int, int>[,] GetOffsets(int[] rows, int[] cols) { var offsets = new Tuple<int, int>[rows.Length, cols.Length]; for (var i = 0; i < rows.Length; i++) for (var j = 0; j < cols.Length; j++) offsets[i, j] = Tuple.Create( rows.Take(i).Sum(), cols.Take(j).Sum() ); return offsets; } public void Fill<T>(T[,] result, T[,][,] m, Tuple<int, int>[,] offsets) { for (var i = 0; i < m.GetLength(0); i++) for (var j = 0; j < m.GetLength(1); j++) Fill(result, m[i, j], offsets[i, j]); } public void Fill<T>(T[,] result, T[,] source, Tuple<int, int> offset) { for (var i = 0; i < source.GetLength(0); i++) for (var j = 0; j < source.GetLength(1); j++) result[offset.Item1 + i, offset.Item2 + j] = source[i, j]; } 
+1
source share

I think you need to provide the array elements corresponding to rowid and column to achieve the problem of indexing the outer matrix. Assuming you already have an Array to Matrix object transform,

Not sure if I understood the rules correctly, but here is what I have done so far:

I implemented the Matrix and MatrixList classes as follows:

  public class Matrix { public int row { get; set; } public int column { get; set; } public double value { get; set; } } public class MatrixList { public List<Matrix> matrixList = new List<Matrix>(); } 

Using these classes, I applied the following algorithm:

  List<MatrixList> matricesMatrix = new List<MatrixList>(); init(matricesMatrix); int totalRows = 10;//as you stated, this is already known int totalColumns = 10;//as you stated, this is already known List<Matrix> ResultMatrix = new List<Matrix>(); foreach (MatrixList matrixListItem in matricesMatrix) { for (int i = 0; i < totalRows; i++) { List<Matrix> matrixItemList = matrixListItem.matrixList.FindAll(s => s.row == i); foreach(Matrix matrixItem in matrixItemList) for (int j = 0; j < totalColumns; j++) { if (matrixItem.column == j) ResultMatrix.Add(new Matrix { row = i, column = j, value = matrixItem.value }); } } } 

where init is an object filling method implemented as follows:

  private void init(List<MatrixList> matricesMatrix) { MatrixList ml = new MatrixList(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { ml.matrixList.Add(new Matrix { row = i, column = j, value = i + j }); } } matricesMatrix.Add(ml); } 

I was in a dummy application for Windows forms, so I used the richtextbox test to verify the code above.

  for (int i = 0; i < totalRows; i++) { foreach (Matrix item in ResultMatrix) { if (item.row == i) { for (int j = 0; j < totalColumns; j++) if (item.column == j) richTextBox1.Text += item.value + " "; } } richTextBox1.Text += Environment.NewLine; } 

and the result:

 0 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 11 3 4 5 6 7 8 9 10 11 12 4 5 6 7 8 9 10 11 12 13 5 6 7 8 9 10 11 12 13 14 6 7 8 9 10 11 12 13 14 15 7 8 9 10 11 12 13 14 15 16 8 9 10 11 12 13 14 15 16 17 9 10 11 12 13 14 15 16 17 18 

I don't have much time to give quite a number of array elements to show beriefly at the moment, but I think you can get this idea.

-one
source share

All Articles