Search code examples
arraysmultidimensional-arraydslice

Slicing 2D arrays in D


If I have a 2D array in D, I know that I can create 1D slices along rows as follows:

auto one_dim_arr=two_dim_arr[i][0..$]

is there a simple way to make a 1D slice along columns? Something that does what one might think

auto one_dim_arr=two_dim_arr[0..$][j]

would do?


Solution

  • Here is what a user-created type for this might look like:

    // Demo
    
    void main()
    {
        int[3][3] arr = [
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9],
        ];
    
        // simple creation
        auto middleColumn = verticalSlice(arr, 1);
        assert(middleColumn[1] == 5);
    
        // iteratable
        foreach (i, v; middleColumn)
            assert(v == 2+i*3);
    
        // still a slice - writing will change original array
        middleColumn[1] = 17;
        assert(arr[1][1] == 17);
    
        // sliceable itself
        auto center = middleColumn[1..2];
        center[0] = 42;
        assert(arr[1][1] == 42);
    
        // get a normal array with .dup
        int[] copyOfMiddleColumn = middleColumn.dup;
    }
    
    // Implementation
    
    struct StepSlice(T)
    {
        T* ptr;
        size_t length, step;
    
        T opIndex(size_t index)
        in { assert(index<length); }
        body { return ptr[step*index]; }
    
        void opIndexAssign(T value, size_t index)
        in { assert(index<length); }
        body { ptr[step*index] = value; }
    
        StepSlice!T opSlice(size_t start, size_t end)
        in { assert(start<=end && end<=length); }
        body { return StepSlice!T(ptr+start*step, end-start, step); }
    
        int opApply(int delegate(ref T) dg)
        {
            int result = 0;
    
            for (size_t i=0; i<length; i++)
            {
                result = dg(ptr[i*step]);
                if (result)
                    break;
            }
            return result;
        }
    
        int opApply(int delegate(ref size_t, ref T) dg)
        {
            int result = 0;
    
            for (size_t i=0; i<length; i++)
            {
                result = dg(i, ptr[i*step]);
                if (result)
                    break;
            }
            return result;
        }
    
        T[] dup()
        {
            T[] result = new T[length];
            for (size_t i=0; i<length; i++)
                result[i] = ptr[i*step];
            return result;
        }
    }
    
    StepSlice!T verticalSlice(T, size_t W)(T[W][] arr, size_t column)
    {
        return StepSlice!T(arr[0].ptr+column, arr.length, W);
    }
    

    I think it's missing range primitives, but still a good starting point.


    With std.range.stride:

    import std.range;
    
    // Demo
    
    void main()
    {
        int[3][3] arr = [
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9],
        ];
    
        // simple creation
        auto middleColumn = verticalSlice(arr, 1);
        assert(middleColumn[1] == 5);
    
        // iteratable
        uint i;
        foreach (v; middleColumn)
            assert(v == 2+(i++)*3);
    
        // still a slice - writing will change original array
        middleColumn[1] = 17;
        assert(arr[1][1] == 17);
    
        // sliceable itself
        auto center = middleColumn[1..2];
        center[0] = 42;
        assert(arr[1][1] == 42);
    
        // get a normal array with array()
        int[] copyOfMiddleColumn = array(middleColumn);
    }
    
    // Implementation
    
    auto verticalSlice(T, size_t W)(T[W][] arr, size_t column)
    {
        T* start = arr[0].ptr+column;
        return stride(start[0..W*arr.length], W);
    }