2

I was just wondering, is there a better way to initialize c# multidimensional array of objects (reference type).

Here is my code:

Board = new Field[BoardHeight, BoardWidth];

for (int i = 0; i < BoardHeight; i++)
{
    for (int j = 0; j < BoardWidth; j++)
    {
        Board[i, j] = new Field();
    }
}

If I could get rid of for/for loops and replace it with single line? That'd be just great.

5
  • 3
    Should be asked on Code Review. Commented Aug 23, 2015 at 1:55
  • 1
    The method looks good. Why don't you make a utility method out of it and use it whenever needed? Commented Aug 23, 2015 at 1:58
  • Can't you just loop through and initialize the array in a flat loop? I think Length will expose the total length of the array, and you can just use modulo over Rank to figure out what indices you need Commented Aug 23, 2015 at 2:11
  • @Asad you can (i.e. for (var i = 0; i < BoardHeight* BoardWidth; i++) { Board[i/BoardWidth, i%BoardWidth] = new Field();}) but it is less readable (and unlikely any faster) Commented Aug 23, 2015 at 2:14
  • @AlexeiLevenkov Yeah, but it makes it easy to extract this into an extension method for arrays of arbitrary rank. You don't need the multiply there btw, I think .Length will work. Commented Aug 23, 2015 at 2:17

4 Answers 4

5

Nested for loops is generally most readable/accepted approach to initialize 2d array.

If you really need single statement - one option is to use Enumerable.Aggregate (to create/fill array in single statement) and Enumerable.Range + Enumerable.SelectMany (to create indexes using How do I take the Cartesian join of two lists in c#?):

 Board = Enumerable.Range(0, BoardHeight)
         .SelectMany(x => Enumerable.Range(0, BoardWidth), (row,col)=>new {row,col})
         .Aggregate(new Field[BoardHeight, BoardWidth],
             (board, position)=> 
               {
                   board[position.row,position.col] = new Field(); 
                   return board;
               });

(Readability of this code is open for discussion)

A bit more practical solution:

for (var i = 0; i < BoardHeight* BoardWidth; i++) 
{
    Board[i / BoardWidth, i % BoardWidth] = new Field();
}

If you really need "single line initialization" - refactor nested for loop into method (possibly generic) and call it wherever you want with "single line". Something like:

   public TField[,] InitializeArray<TField>(
      int BoardHeight, int BoardWidth) where TField:new()
   {
        var Board = new TField[BoardHeight, BoardWidth];

        for (int i = 0; i < BoardHeight; i++)
        {
            for (int j = 0; j < BoardWidth; j++)
            {
                Board[i, j] = new TField();
            }
        }
        return Board;
}
Sign up to request clarification or add additional context in comments.

2 Comments

wow, that's quite ugly. I would never use code like this in any serious codebase. But yeah, solves OPs question.
@MarcinJuraszek Sure. I would not use it for any real code, but as exercise for single line :) - I just like Aggregate but never find a place to use it in real code.
1

More for curiosity's sake than anything else, I adapted some old code into an extension method that will initialize every member of an array of arbitrary rank with the results of a provided Func. You'd use it like this (with 2D, 3D, whatever-D arrays):

var board = new Foo[10, 20];
board.Fill(() => new Foo());

Here's the extension method:

static class Extensions
{
    public static void Fill(this Array arr, Func<object> gen)
    {
        // Get the dimensions of the array
        var dims = Enumerable.Range(0, arr.Rank)
            .Select(arr.GetLength)
            .ToArray();

        Func<int, int, int> product = (i1, i2) => i1 * i2;

        for (var i = 0; i < arr.Length; i++)
        {
            var indices = dims.Select((d, n) => (i/dims.Take(n).Aggregate(1, product))%d).ToArray();
            arr.SetValue(gen(), indices);
        }
    }
}

Comments

1

Thanks to MarcinJuraszek and Alexei Levenkov suggestions I came out with this answer. Technically is not a single statement (all in all), but if you hide extension method ;-) it will look very clean:

Board = new Field[BoardHeight, BoardWidth];
Board.Init();

Hide this somewhere:

public static class MyExtensions
{
    public static void Init<T>(this T[,] board) where T : new()
    {
        for (int i = 0; i < board.GetLength(0); i++)
        {
            for (int j = 0; j < board.GetLength(1); j++)
            {
                board[i,j] = new T();
            }
        }
    }
}

Comments

0

I think this is the easiest one-liner (split to multiple lines for readability) to initialize your array:

var board = Enumerable.Range(0, BoardWidth)
    .Select(row => Enumerable.Range(0, BoardHeight)
        .Select(value =>new TField())
        .ToArray())
    .ToArray();

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.