Two Dimensional Array Slice in C#

Two dimensional array slice in C#

There's no direct "slice" operation, but you can define an extension method like this:

public static IEnumerable<T> SliceRow<T>(this T[,] array, int row)
{
for (var i = 0; i < array.GetLength(0); i++)
{
yield return array[i, row];
}
}

double[,] prices = ...;

double[] secondRow = prices.SliceRow(1).ToArray();

Take slice from 2D array (int[ , ]) using LINQ in C#

@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();
}

2D array slicing

When you do this:

var slice = matrix[0..2][0..2];

You are actually slicing your array twice. It's equivalent to writing:

var temp = matrix[0..2];
var slice = temp[0..2];

Which in this case, makes the second slice redundant. You can confirm this by writing:

var slice = matrix[0..2][0..3]

In this case, the second slice will throw an exception because it is larger than the first slice.

What I think you are trying to do is this:

foreach (var item in matrix[0..2].SelectMany(x => x[0..2]))
{
}

How to get a dimension (slice) from a multidimensional array

No. You could of course write a wrapper class that represents a slice, and has an indexer internally - but nothing inbuilt. The other approach would be to write a method that makes a copy of a slice and hands back a vector - it depends whether you want a copy or not.

using System;
static class ArraySliceExt
{
public static ArraySlice2D<T> Slice<T>(this T[,] arr, int firstDimension)
{
return new ArraySlice2D<T>(arr, firstDimension);
}
}
class ArraySlice2D<T>
{
private readonly T[,] arr;
private readonly int firstDimension;
private readonly int length;
public int Length { get { return length; } }
public ArraySlice2D(T[,] arr, int firstDimension)
{
this.arr = arr;
this.firstDimension = firstDimension;
this.length = arr.GetUpperBound(1) + 1;
}
public T this[int index]
{
get { return arr[firstDimension, index]; }
set { arr[firstDimension, index] = value; }
}
}
public static class Program
{
static void Main()
{
double[,] d = new double[,] { { 1, 2, 3, 4, 5 }, { 5, 4, 3, 2, 1 } };
var slice = d.Slice(0);
for (int i = 0; i < slice.Length; i++)
{
Console.WriteLine(slice[i]);
}
}
}

C#: Is an extension method for getting 2D slice of 3D array possible?

Those two posts show that you can get a one dimensional IEnumerable<T> slice out of a 2D array, but for a slice of a 3D array, you would get a 2D IEnumerable<IEnumerable<T>> slice.

public static IEnumerable<IEnumerable<T>> SliceLayer<T>(this T[,,] array, int lay)
{
IEnumerable<T> SliceRow(int row) {
for (var j = array.GetLowerBound(2); j <= array.GetUpperBound(2); j++)
{
yield return array[lay, row, j];
}
}

for (var i = array.GetLowerBound(1); i <= array.GetUpperBound(1); i++)
{
yield return SliceRow(i);
}
}

However, it seems like the caller expects a 2D array (double[,]), rather than an IEnumerable. In that case you can't use iterator blocks.

public static T[,] SliceLayer<T>(this T[,,] array, int lay)
{
T[,] result = new T[array.GetLength(1), array.GetLength(2)];
var lowerBound1 = array.GetLowerBound(1);
var lowerBound2 = array.GetLowerBound(2);
for (var i = lowerBound1; i <= array.GetUpperBound(1); i++)
{
for (var j = lowerBound2; j <= array.GetUpperBound(2); j++)
{
// you could just use i and j as indices here if the input
// arrays is guaranteed to have non-negative lower bounds
result[i - lowerBound1, j - lowerBound2] = array[lay, i, j];
}
}
return result;
}

Array slices in C#

Arrays are enumerable, so your foo already is an IEnumerable<byte> itself.
Simply use LINQ sequence methods like Take() to get what you want out of it (don't forget to include the Linq namespace with using System.Linq;):

byte[] foo = new byte[4096];

var bar = foo.Take(41);

If you really need an array from any IEnumerable<byte> value, you could use the ToArray() method for that. That does not seem to be the case here.

Unsafe slicing a 2D array with ReadOnlySpanT

After creating a Span<T>, ReadOnlySpan<T> or Memory<T> all subsequent uses are safe.

Here's a reference by Stephen Toub.

First, Span is a value type containing a ref and a length, defined approximately as follows:

public readonly ref struct Span<T>
{
private readonly ref T _pointer;
private readonly int _length;
...
}

The concept of a ref T field may be strange at first—in fact, one can’t actually declare a ref T field in C# or even in MSIL. But Span is actually written to use a special internal type in the runtime that’s treated as a just-in-time (JIT) intrinsic, with the JIT generating for it the equivalent of a ref T field.

Span is a ref-like type as it contains a ref field, and ref fields can refer not only to the beginning of objects like arrays, but also to the middle of them (...) These references are called interior pointers, and tracking them is a relatively expensive operation for the .NET runtime’s garbage collector. As such, the runtime constrains these refs to only live on the stack, as it provides an implicit low limit on the number of interior pointers that might be in existence.

So the GC actually does track the pointers from your ReadOnlySpan<T>, so after being constructed spans are always safe. The span will always point to the array you sliced and it doesn't matter where you return it. Implementation details on how exactly it's done are specific to the CLR. Keywords to search for are "managed pointers" and "interior pointers". I recommend this article if you want to get more nitty-gritty.

Slicing a SpanT row from a 2D matrix - not sure why this works

It seems to me that the crux of your confusion is here:

Shouldn't the ref modifier only get a reference to the local copy of that float value returned to the method, and not a reference to the actual value stored within the matrix?

You seem to be under the mistaken impression that the indexer syntax for an array works exactly the same as for other types. But it doesn't. An indexer for an array is a special case in .NET, and treated as a variable, not a property or pair of methods.

For example:

void M1()
{
int[] a = { 1, 2, 3 };

M2(ref a[1]);
Console.WriteLine(string.Join(", ", a);
}

void M2(ref int i)
{
i = 17;
}

yields:

1, 17, 3

This works because the expression a[1] is not a call to some indexer getter, but rather describes a variable that is physically located in the second element of the given array.

Likewise, when you call DangerousCreate() and pass ref m[row, 0], you are passing the reference to the variable that is exactly the element of the m array at [row, 0].

Since a reference to the actual memory location is what's being passed, the rest should be no surprise. That is, that the Span<T> class is able to then use that address to wrap a specific subset of the original array, without allocating any extra memory.



Related Topics



Leave a reply



Submit