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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.