6

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?

4
  • double[,,,] arrayToFill = {{1,2,3,4},{7,8,9,10}}; Commented Aug 18, 2022 at 10:40
  • There is no such method. Pretty much all Array methods work only on 1D arrays. The obvious option is to write your own extension method and then you can call that on any multidimensional array you want. You could overload it to handle various ranks with explicit loops or you could write a more complex method that could handle any rank. Commented Aug 18, 2022 at 10:40
  • There is already an answer here: stackoverflow.com/questions/11672149/… but they use unsafe code. And I agree with the conclusion on the best answer. If it is not performance critical then don't. Commented Aug 18, 2022 at 10:44
  • I was going to write some extension methods but I see that's already been done in one of the answers on the question linked above, so I'll leave it at that. Commented Aug 18, 2022 at 10:47

3 Answers 3

9

(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

Sign up to request clarification or add additional context in comments.

8 Comments

I wrote this extension method just recently as an answer to a different question stackoverflow.com/a/73373888/14868997, it's very very fast. Personally I would just leave it with an AsSpan extension and do array.AsSpan.Fill
I'll add that link to my answer!
To avoid needing to specify type explicitly, you'd need an extension method for each possible rank, as noted in that link
@Charlieface I undid your edit because it does NOT need to have the type specified in the case where the type of the fill value is correct, because it infers the type of T from the fill value type.
Ah good point. But sounds risky, instead probably better to have separate extensions for each rank. arrayToFill.Fill(1); sounds way to much of a risk to leave as it is.
|
3

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).

4 Comments

What's your point exactly? I posted this before MatthewWatson edited from the words "IMPORTANT!..." onwards
apologies, don't have a point exactly. I was just musing on your comment and reusing an AsSpan() extension rather than copying the internals. I'm now wondering, could this be a more generic extension that works with any given managed struct?
Yes you could use a DateTime[] also, and it doesn't have to be a struct. This only works on arrays of values, if that's what you were wondering, because that wouldn't make any sense to do it on a single value. Yes your example is a bit neater if you want all ranks.
-1

its not loops are amazing on arrays you just got to get used to it.

how to fill a Multi-dim array;

int **inpu**;
for(i...;i++){
  for(j...;j++)
  {
   //here you can set your input value how ever you want
   //input  = i + j; or cin << **input**;
   array[i][j] = **inpu**;
  }
}

some times its better using loops than using a unknown function that probably is using loops to fill your arrays and takes more time the finish the task.

1 Comment

the question was about without using loops -1

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.