Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filling a multidimensional array in c# without loops with single value

Is there a way in C# to set each value of a multidimensional array to a specific value without using loops? I found Array.Fill but it seems to work only for a 1D-Array.

Basically what I'm looking for is something like:

double[,,,] arrayToFill = new double[7,8,9,10];
Array.FillWhole(arrayToFill, 1.2345);

Of course one could use nested loops but this looks annoying. Maybe there is a better solution?

like image 772
ewiigno Avatar asked Sep 07 '26 14:09

ewiigno


2 Answers

(See the last code sample in this answer for the best solution.)

You can actually use a Span<T> to do this, but it looks quite fiddly!

double[,,,] arrayToFill = new double[7, 8, 9, 10];

var data = MemoryMarshal.CreateSpan(
    ref Unsafe.As<byte, double>(ref MemoryMarshal.GetArrayDataReference(arrayToFill)),
    arrayToFill.Length);

data.Fill(1.2345);

foreach (var value in arrayToFill)
{
    Console.WriteLine(value);  // All values are 1.2345
}

You can write a method to encapsulate this:

public static void FillArray<T>(Array array, T value)
{
    var data = MemoryMarshal.CreateSpan(
        ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
        array.Length);

    data.Fill(value);
}

Then the call site becomes a lot more readable:

double[,,,] arrayToFill = new double[7, 8, 9, 10];

FillArray(arrayToFill, 1.2345);

foreach (var value in arrayToFill)
{
    Console.WriteLine(value);  // All values are 1.2345
}

If you want to get more fancy you can encapsulate this in an extension method:

public static class ArrayExt
{
    public static void Fill<T>(this Array array, T value)
    {
        var data = MemoryMarshal.CreateSpan(
            ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
            array.Length);

        data.Fill(value);
    }
}

Then you can call it like so:

double[,,,] arrayToFill = new double[7, 8, 9, 10];

arrayToFill.Fill(1.2345);

foreach (var value in arrayToFill)
{
    Console.WriteLine(value);  // All values are 1.2345
}

IMPORTANT! You must ensure that you use the correct type with which to fill the array. If you specify the wrong type it will fill the array with rubbish, for example:

arrayToFill.Fill(1);

will really mess things up.

In this example you'd need to do arrayToFill.Fill<double>(1); to specify the correct type because otherwise it will infer the wrong type for filling the array.

As per comments from /u/charlieface, you can circumvent this issue by adding strongly-typed overloads for each array dimension, thusly:

This is probably the best approach:

public static class ArrayExt
{
    public static void Fill<T>(this T[,]    array, T value) => fill(array, value);
    public static void Fill<T>(this T[,,]   array, T value) => fill(array, value);
    public static void Fill<T>(this T[,,,]  array, T value) => fill(array, value);
    public static void Fill<T>(this T[,,,,] array, T value) => fill(array, value);

    static void fill<T>(Array array, T value)
    {
        var data = MemoryMarshal.CreateSpan(
            ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
            array.Length);

        data.Fill(value);
    }

    // ...etc etc. But does anyone really need more than a 5D array? ;)
}

Then arrayToFill.Fill(1); will work correctly.


Also see this post from /u/charlieface

like image 92
Matthew Watson Avatar answered Sep 10 '26 04:09

Matthew Watson


As mentioned by @MatthewWatson, my extension method in a different answer allows you to get a Span<T> of a two dimensional array.

But it's risky, as it leaves it open to the caller as to which data type to actually fill it with. Instead, create the extension methods with the exact rank you need, for example T[,,]

public static Span<T> AsSpan<T>(this T[,,] array)
{
    return MemoryMarshal.CreateSpan(ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)), array.Length);
}

Then you use Span.Fill

double[,,,] arrayToFill = new double[7,8,9,10];
arrayToFill.AsSpan().Fill(1.2345);

You need a different extension for each rank, such as AsSpan<T>(this T[,] array) and AsSpan<T>(this T[,,] array).

like image 43
Charlieface Avatar answered Sep 10 '26 02:09

Charlieface



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!