0

For an example if I want to iterate test and perfrorm operations on elements from that array but they have to be formatted in a particular way.

Essentially I am trying to loop over a 2d array using a 2d array.

double[,] test = {
    {9, 8, 7, 6, 5, 4, 3, 2},
    {8, 7, 6, 5, 4, 3, 2, 1},
    {7, 6, 5, 4, 3, 2, 1, 0},
    {6, 5, 4, 3, 2, 1, 0, 0},
    {5, 4, 3, 2, 1, 0, 0, 0},
    {4, 3, 2, 1, 0, 0, 0, 0},
    {3, 2, 1, 0, 0, 0, 0, 0},
    {2, 1, 0, 0, 0, 0, 0, 0},
};

double[,] subset = new double[2,2]; //used in math

What I would like to be able to do is to iterate over any size matrix (assuming that they are even sized and square) with each iteration looking like this:

Iteration 1:
subset[0,0] = test[0,0];
subset[0,1] = test[0,1];
subset[1,0] = test[1,0];
subset[1,1] = test[1,1];

So basically it selected a square same size as subset out of the large matrix.

Iteration 2:
subset[0,2] = test[0,2];
subset[1,2] = test[1,2];
subset[0,3] = test[0,3];
subset[1,3] = test[1,3];
4
  • 1
    You can write an extension method that utilizes yield return (msdn.microsoft.com/en-us/library/9k7k7cf0.aspx). Basically, the extension method will iterate over the array and calls yield return on a subset of the array each iteration. Using this, you can customize how big and which indices will be included in the subset. With that said, you can take the extension method a step further and make each "iteration" smarter by using offsets to skip over chunks of the array that were yield returned already. Commented Mar 8, 2016 at 22:18
  • Thanks, I will go and read up on it. Commented Mar 8, 2016 at 22:19
  • If I understand correctly, you want to iterate over your matrix in "chunks" of 2x2 (or generally - nxm) sub-matrices. What should happen if the parent matrix's size in either direction isn't a multiple of the requested sub-matrix's dimension? In your example you're trying to chunk an 8x8 matrix into 2x2 sub-matrices, but what should happen if it was 9x9 instead? Commented Mar 8, 2016 at 22:40
  • I am resizing my data to be N == M; M % subM = 0; Commented Mar 8, 2016 at 22:47

3 Answers 3

2

You can do this via an extension method. A few things worth mentioning:

  • Using Array.Copy as opposed to manually assigning the elements should yield better performance.
  • Like Tom A mentioned in a comment, you should use yield return to create an IEnumerable. You can then iterate over it using a foreach loop, or perform other operations.

Implementation:

static class MatrixExtensions
{
    public static IEnumerable<T[,]> ChunkMatrix<T>(this T[,] inputMatrix, int chunkWidth, int chunkHeight)
    {
        int inputWidth = inputMatrix.GetLength(0);
        int inputHeight = inputMatrix.GetLength(1);

        for(int i = 0; i < inputWidth; i += chunkWidth)
        {
            for(int j = 0; j < inputHeight; j += chunkHeight)
            {
                T[,] chunk = new T[chunkWidth, chunkHeight];
                for(int k = 0; k < chunkWidth; k++)
                {
                    int sourceIndex = i*inputWidth + k* inputWidth + j;
                    var destinationIndex = k* chunkHeight;
                    Array.Copy(inputMatrix, sourceIndex, chunk, destinationIndex, chunkHeight);
                }
                yield return chunk;
            }
        }
    }
}

Usage:

double[,] test = {
    {1,  2,  3,  4,  5,  6,  7,  8},
    {9,  10, 11, 12, 13, 14, 15, 16},
    {17, 18, 19, 20, 21, 22, 23, 24},
    {25, 26, 27, 28, 29, 30, 31, 32},
    {33, 34, 35, 36, 37, 38, 39, 40},
    {41, 42, 43, 44, 45, 46, 47, 48},
    {49, 50, 51, 52, 53, 54, 55, 56},
    {57, 58, 59, 60, 61, 62, 63, 64},
};

foreach(double[,] chunk in test.ChunkMatrix(2, 2))
{
    // First iteration:
    // 1 2
    // 9 10
    // 
    // Second iteration:
    // 3 4
    // 11 12
    //
    // ...
}

I changed your test data to not include duplicate values, as to better illustrate the effect.

It should be noted that my implementation will not work correctly on matrices with dimensions that are not multiples of the chunks' dimensions, as it was mentioned in a comment that this will never be the case. If needed, modifying it to account for this scenario shouldn't be too hard.

Sign up to request clarification or add additional context in comments.

1 Comment

We are expected to figure out the languages we use on the fly. Thanks for the example, this is a really cool feature of C#. I do have to ask. How would you go about putting the parts back together using this technique? Assuming they are in the same order but slightly changed value-wise.
0

Try this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        const int COLUMNS = 8;
        const int ROWS = 8;
        const int SIZE = 2;
        static void Main(string[] args)
        {
            double[,] test = {
                {9, 8, 7, 6, 5, 4, 3, 2},
                {8, 7, 6, 5, 4, 3, 2, 1},
                {7, 6, 5, 4, 3, 2, 1, 0},
                {6, 5, 4, 3, 2, 1, 0, 0},
                {5, 4, 3, 2, 1, 0, 0, 0},
                {4, 3, 2, 1, 0, 0, 0, 0},
                {3, 2, 1, 0, 0, 0, 0, 0},
                {2, 1, 0, 0, 0, 0, 0, 0},
            };

            for (int i = 0; i < COLUMNS; i += SIZE)
            {
                for (int j = 0; j < ROWS; j += SIZE)
                {
                    for (int k = j; k < j + SIZE; k++)
                    {
                        for (int l = i; l < i + SIZE; l++)
                        {
                            Console.WriteLine("test[{0}, {1}] = {2}", k, l, test[k, l]);
                        }
                    }
                }
            }
            Console.ReadLine();
        }
    }
}

Comments

0

I am not saying this is the best solution but while I look at the yield statement I managed to get it working using this method.

public static double[,] GetMapSection(Rectangle area, double[,] map) {
    double[,] result = new double[area.Width, area.Height];

    for (Int32 y = 0; y < area.Height; ++y) {
        for (Int32 x = 0; x < area.Width; ++x) {
            result[x, y] = map[x + area.X, y + area.Y];
        }
    }
    return result;
}

I call it via:

List<double[,]> testChannel = new List<double[,]>();
for (int i = 0; i < Math.Sqrt(large_mapdata.Length); i+=8) {            
    for (int j = 0; j < Math.Sqrt(large_mapdata.Length); j+=8) {
        testChannel.Add(GetMapSection(new Rectangle(i, j, 8, 8), large_mapdata));
    }
}

In this example I am creating 8x8 chunks out of an array that is 32x32 in size. I can confirm that this worked for me and is cleaner than what I had before.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.