Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array operations with n-dimensional array using LINQ (C#)

Tags:

arrays

c#

linq

Assume we have a jagged array

int[][] a = { new[] { 1, 2, 3, 4 }, new[] { 5, 6, 7, 8 }, new[] { 9, 10, 11, 12 } };

To get a sum of second row and sum of second column, it can be written both code lines respectively:

int rowSum = a[1].Sum();
int colSum = a.Select(row => row[1]).Sum();

But if we have definition of 2-dimensional array

int[,] a = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };

the above-cited code will not work due to compiller errors:

Error   1   Wrong number of indices inside []; expected 2
Error   2   'int[*,*]' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'int[*,*]' could be found (are you missing a using directive or an assembly reference?)

So, the question: How to use LINQ methods with n-dimensional arrays, but not jagged ones? And is where a method to convert rectangular array to jagged?

P.S. I tried to find the answer in documentation, but without result.

like image 322
Lev Sharansky Avatar asked Sep 06 '11 11:09

Lev Sharansky


3 Answers

LINQ to Objects is based on the IEnumerable<T> Interface, i.e. a one-dimensional sequence of values. This means it doesn't mix well with n-dimensional data structures like non-jagged arrays, although it's possible.

You can generate one-dimensional sequence of integers that index into the n-dimensional array:

int rowSum = Enumerable.Range(0, a.GetLength(1)).Sum(i => a[1, i]);

int colSum = Enumerable.Range(0, a.GetLength(0)).Sum(i => a[i, 1]);
like image 89
dtb Avatar answered Oct 27 '22 11:10

dtb


About your question "How to use LINQ methods with n-dimensional arrays":

You can't use most LINQ methods with a n dimensional array, because such an array only implements IEnumerable but not IEnumerable<T> and most of the LINQ extension methods are extension methods for IEnumerable<T>.

About the other question: See dtb's answer.

like image 41
Daniel Hilgarth Avatar answered Oct 27 '22 10:10

Daniel Hilgarth


To add to dtb's solution, a more general way of iterating over all items of the array would be:

int[,] b = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };

var flattenedArray = Enumerable.Range(0, b.GetLength(0))
                     .SelectMany(i => Enumerable.Range(0, b.GetLength(1))
                         .Select(j => new { Row = i, Col = j }));

And now:

var rowSum2 = flattenedArray.Where(t => t.Row == 1).Sum(t => b[t.Row, t.Col]);
var colSum2 = flattenedArray.Where(t => t.Col == 1).Sum(t => b[t.Row, t.Col]);

Of course this is ultra-wasteful as we are creating coordinate tuples even for those items that we will end up filtering out with Where, but if you don't know what the selection criteria will be beforehand this is the way to go (or not -- this seems more like an excercise than something you 'd want to do in practice).

I can also imagine how this might be extended for arrays of any rank (not just 2D) using a recursive lambda and something like Tuple, but that crosses over into masochism territory.

like image 35
Jon Avatar answered Oct 27 '22 10:10

Jon