1

I am not C# expert, and total LINQ beginner, having searched a bit in SO and Google without discovering how to do the following:

If I have, say, int[10,10] array, how can I get a 2D slice from it?

For example, if the values in the said array were dependent on their position (a[2,3] = 23, a[4,8] = 48, etc.), I'd like to perform the following pseudocode:

int[3,3] a_slice = slicer_method(a, 3, 6, 2, 5)   // or anything equivalent to this

> [[ 32, 33, 34],
   [ 42, 43, 44],
   [ 52, 53, 54]]

It doesn't have specifically to use LINQ, but I've seen LINQ used in every similar operation I've come across lately.

4 Answers 4

4

@JaredPar is correct, there is no intrinsic way to do slices - that said, you can craft up an extension method to do so:

public static class Ext
{
    public static T[] Slice<T>(this T[] source, int fromIdx, int toIdx)
    {
        T[] ret = new T[toIdx - fromIdx + 1];
        for(int srcIdx=fromIdx, dstIdx = 0; srcIdx <= toIdx; srcIdx++)
        {
            ret[dstIdx++] = source[srcIdx];
        }
        return ret;
    }
    public static T[,] Slice<T>(this T[,] source, int fromIdxRank0, int toIdxRank0, int fromIdxRank1, int toIdxRank1)
    {
        T[,] ret = new T[toIdxRank0 - fromIdxRank0 + 1, toIdxRank1 - fromIdxRank1 + 1];

        for(int srcIdxRank0=fromIdxRank0, dstIdxRank0 = 0; srcIdxRank0 <= toIdxRank0; srcIdxRank0++, dstIdxRank0++)
        {        
            for(int srcIdxRank1=fromIdxRank1, dstIdxRank1 = 0; srcIdxRank1 <= toIdxRank1; srcIdxRank1++, dstIdxRank1++)
            {
                ret[dstIdxRank0, dstIdxRank1] = source[srcIdxRank0, srcIdxRank1];
            }
        }
        return ret;
    }
}

And a test:

void Main()
{
    var singleArr = new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
    singleArr.Slice(2, 4).Dump();
    var doubleArr = new int[,]
    {
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
    };  
    doubleArr.Slice(2, 4, 2, 4).Dump();
}
Sign up to request clarification or add additional context in comments.

3 Comments

This is a nice answer, with example working code, and a nice syntax too. I'll test it as soon as I have some time, thanks!!
Yeah, the only problem with this approach is that "auto-discovery" of multi-dimensional arrays is poorly supported in C#, so you'd have to come up with a variant for each rank combination...I suppose you could generalize it a bit, but not perfectly.
Accepted due to more detail and the explicit mention that this is an extension method (which I haven't used yet but sure will going to take a look).
4

There is no way to do this on the CLR because it doesn't support the notion of array slices. They best you can do is create a wrapper type over arrays that simulates slices

4 Comments

Yeah, I didn't mean actual slices, I just want "some way" to end up with a smaller-sized 2-dimensional array which contains the value of a given "inner rectangle" of a larger array, something similar to selecting a rectangle from an image (although my data is not image-related).
I edited the question so that it doesnt give the idea that I want an array method, but instead any routine that gives back a sub-array, taking the larger array as a parameter.
@heltonbiker Just create a new array with the dimensions provided and use a double for loop to copy the info. That will be the easiest and most performant method, by far. Using LINQ (in this context) will most likely result in messier and less performant code. It tends to not play all that well with multi-dimensional arrays in general. If this was returning a jagged array instead, then LINQ could be useful.
Thank you for your kind explanation! I'll probably accept some answer similar to your suggestion (double for loop).
4

You can try something like this:

public T[,] Slice<T>(T[,] a, int x1, int y1, int x2, int y2)
{
    var result = new T[x2 - x1, y2 - y1];
    for (var i = x1; i < x2; i++)
    {
        for (var j = y1; j < y2; j++)
        {
            result[i - x1, j - y1] = a[i,j];
        }
    }
    return result;
}

sample

2 Comments

I liked this, although it is the trivial way to do it. +1, and I'll take a look at the other answers, too. Thanks!
I accepted JerKimball answer which is more detailed, but your answer is practically identical, with the advantage of a cleaner syntax. Thank you very much!
0
  public class MyArraySlice<T>  where T:struct {
    public MyArraySlice(T[,] array, int xMin, int xMax, int yMin, int yMax) {
      Array = array;
      XMin = xMin; XMax = xMax;
      YMin = yMin; YMax = yMax;
    }

    public T this[int i, int j] { get {
        if (XMin <= i && i < XMax  && YMin <= j && j < YMax)
          return Array[i+XMin, j+YMin];

        throw new ArgumentOutOfRangeException();
      }
    }

    T[,] Array;
    int XMin;
    int XMax;
    int YMin;
    int YMax;
  }

1 Comment

Why add where T : struct? There's nothing about this code that prevents a class from being appropriate. Oh, and your bounds checking in the indexer is off, but that's easily fixed.

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.