Home > Software engineering >  Use c# `Index` and `Range` with jagged arrays
Use c# `Index` and `Range` with jagged arrays

Time:10-30

I am trying to use Index and Range objects to slice a jagged array. But in the example below I cannot extract columns, only rows.

var A =  new float[3][];
A[0] = new float[] { 1.1f, 1.2f, 1.3f };
A[1] = new float[] { 2.1f, 2.2f, 2.3f };
A[2] = new float[] { 3.1f, 3.2f, 3.3f };

// This works as intended to extract rows
var A_row1 = A[0][..];      // { 1.1, 1.2, 1.3 } 
var A_row3 = A[^1][..];     // { 3.1, 3.2, 3.3 } 
var A_row23 = A[1..^0][..]; // { { 2.1, 2.2, 2.3}, { 3.1, 3.2, 3.3 } } 

// This does not work as intended to extract columns
var A_col1 = A[..][0];      // { 1.1, 1.2, 1.3 } ** WRONG
var A_col3 = A[..][^1];     // { 3.1, 3.2, 3.3 } ** WRONG
var A_col23 = A[..][1..^0]; // { { 2.1, 2.2, 2.3}, { 3.1, 3.2, 3.3 } } ** WRONG

var A_sub22 = A[0..2][0..2];
// { { 1.1, 1.2, 1.3 }, { 2.1, 2.2, 2.3 } } ** WRONG
// { { 1.1, 1.2 }, { 2.1, 2.2 } } <= WHAT I AM EXPECTING

Why is it that A[..][0] returns the exact same result as A[0][..]? And why doesn't the last statement contain 2 columns per row, but 3?

The bigger picture here is that I am building a Matrix object that stores the numeric data in a jagged array of double and I want to implement slicing using Index and Range. I thought I could have it support slicing with the following methods

public class Matrix 
{
    readonly double[][] Elements;

    ///<summary>Reference a single element</summary>
    public ref double this[Index row, Index column] 
        => ref Elements[row][column];

    ///<summary>Extract a sub-matrix</summary>
    public double[][] this[Range rows, Range columns]
        => Elements[rows][columns];

    ///<summary>Extract a row sub-vector</summary>
    public double[] this[Index row, Range columns]
        => Elements[row][columns];

    ///<summary>Extract a column sub-vector</summary>
    public double[] this[Range rows, Index column] 
        => Elements[rows][column];
}

But this failed spectacularly in my unit testing. It seems that even if C# supports the syntax, the results are unexpected.

Is there a way to implement the functionality I want to have using the existing framework for slicing that C# is trying to implement?


Sorry, I come from a Fortran background where slicing is natural.

Also, am I the only one that finds it very confusing that the last element in an array is indexed as [^1] but the range to the last element is [..^0]. It seems very inconsistent on the part of Microsoft to have ^0 mean different things for Index as it does for Range.

CodePudding user response:

The columns are three different arrays. The slicing operators do not work across those different arrays, only within one array.

The only slicing you are doing is on the row array and returns the column arrays according to the operators.

A[0][..] returns the first array of A (which is the first column). [..] then returns the whole column. A[..][0] returns the whole array (which is A again) and then the first entry in A, which is again the first column.

CodePudding user response:

This is how I implemented slicing with a jagged array

public class Matrix 
{
    readonly double[][] Elements;

    /// <summary>
    /// Reference an element of the matrix
    /// </summary>
    /// <param name="row">The row index.</param>
    /// <param name="column">The column index.</param>
    public ref double this[Index row, Index column] 
        => ref Elements[row][column];

    /// <summary>
    /// Extract a sub-matrix.
    /// </summary>
    /// <param name="rows">The range of rows.</param>
    /// <param name="columns">The range of columns.</param>
    public double[][] this[Range rows, Range columns]
    {
        get
        {
            var slice = Elements[rows];
            for (int i = 0; i < slice.Length; i  )
            {
                slice[i] = slice[i][columns];
            }
            return slice;
        }
    }

    /// <summary>
    /// Extract a row sub-vector.
    /// </summary>
    /// <param name="row">The row index.</param>
    /// <param name="columns">The range of columns.</param>
    public double[] this[Index row, Range columns]
    {
        get
        {
            var slice = Elements[row];
            var result = slice[columns];
            return result;
        }
    }

    /// <summary>
    /// Extract a column sub-vector.
    /// </summary>
    /// <param name="rows">The range of rows.</param>
    /// <param name="column">The column index.</param>
    public double[] this[Range rows, Index column]
    {
        get
        {
            var slice = Elements[rows];
            var result = new double[slice.Length];
            for (int i = 0; i < slice.Length; i  )
            {
                result[i] = slice[i][column];
            }
            return result;
        }
    }

}
  • Related