C# Question

# How to group by portions of a grid by quadratic amounts?

I'm facing the following problem: The have one grid and each grid cell has a position. One example of such grid could be the following one:

``````_________________________________________
|       |       |       |       |       |
| (0,0) | (1,0) | (2,0) | (3,0) | (4,0) |
|_______|_______|_______|_______|_______|
|       |       |       |       |       |
| (0,1) | (1,1) | (2,1) | (3,1) | (4,1) |
|_______|_______|_______|_______|_______|
|       |       |       |       |       |
| (0,2) | (1,2) | (2,2) | (3,2) | (4,2) |
|_______|_______|_______|_______|_______|
|       |       |       |       |       |
| (0,3) | (1,3) | (2,3) | (3,3) | (4,3) |
|_______|_______|_______|_______|_______|
|       |       |       |       |       |
| (0,4) | (1,4) | (2,4) | (3,4) | (4,4) |
|_______|_______|_______|_______|_______|
``````

I need to create a function that, given a number N, creates groups of quadratic NxN amounts of that number at most. For instance, for N = 2, each group would contain at most a 2x2 cells, group 1 would contain [(0, 0) (1, 0) (0, 1) (1, 1)], group 2 [(2, 0) (3, 0) (2, 1) (3, 1)], group 3 would just contain [(4, 0) (4, 1)] and so on.

I'm using C#, so this is conceptually a Group by operation, I decided to use LINQ Group By function, however it requires a lambda expression that must calculate a number that must be the same for each group. Therefore I'm looking at an expression that, for my problem with n = 2, must return the same number for [(0, 0) (1, 0) (0, 1) (1, 1)], another number for [(2, 0) (3, 0) (2, 1) (3, 1)], a different one for [(4, 0) (4, 1)], etc...

Which kind of expression could fulfil that property?

Thank you

In order to use a 'Group By'-operation, you need to define a key to group on. In this case, if it's about a grid, the only possible key I could think of is the result of a calculation based on the index of the row/column in the grid. I find it a bit difficult to explain the calculation I chose in plain text so I hope the example below does the talk for me.

Test data / settings

``````var grid = new List<List<string>>();

grid.Add(new List<string>(new[] { "0,0", "1,0", "2,0", "3,0", "4,0" }));
grid.Add(new List<string>(new[] { "0,1", "1,1", "2,1", "3,1", "4,1" }));

grid.Add(new List<string>(new[] { "0,2", "1,2", "2,2", "3,2", "4,2" }));
grid.Add(new List<string>(new[] { "0,3", "1,3", "2,3", "3,3", "4,3" }));

grid.Add(new List<string>(new[] { "0,4", "1,4", "2,4", "3,4", "4,4" }));

int n = 2;
``````

Solution 1 - Lambda expression

``````var result_1 = grid

// Create quadratic groups by calculating the combined index of the row+column with the quadratic group factor.
.SelectMany(r =>
r.GroupBy(c =>
(int)Math.Floor((double)grid.IndexOf(r) / (double)n)
+ "_" +
(int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)

// Combine all same keys together in one group.
.GroupBy(g => g.Key)

// Get all results per group.
.Select(gg => gg.SelectMany(g => g).ToList())

// ToList() because it's easier to inspect the value of the result while debugging.
.ToList();

// Short version:
var result_2 = grid
.SelectMany(r =>
r.GroupBy(c =>
(int)Math.Floor((double)grid.IndexOf(r) / (double)n) + "_" + (int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)
.GroupBy(g => g.Key)
.Select(gg => gg.SelectMany(g => g).ToList())
.ToList();
``````

Solution 2 - Oldschool loop, probably easier/better to understand.

``````var result_3 = new List<List<string>>();

// Range (amount of both 'rows' and 'columns' since it's a grid).
int range = (int)Math.Ceiling((double)grid.Count / (double)n);

// Loop through 'rows'.
for(var y = 0; y < range; y++)
{
int skipRowsAmount = (y * n);
int takeRowsAmount = n;

// Get all 'rows' to split in groups.
var rows = grid.Skip(skipRowsAmount).Take(takeRowsAmount).ToList();

// Loop through 'columns'.
for (var x = 0; x < range; x++)
{
int skipColumnsAmount = (x * n);
int takeColumnsAmount = n;

// Get all 'columns' from all 'rows' to split in groups.
var quadraticColumns = rows.SelectMany(l => l.Skip(skipColumnsAmount).Take(takeColumnsAmount)).ToList();