-1

I have an Array of unknown dimensions. E.g. it may be object[], object[,], object[,,,].

I want to fill it sequentially (e.g. for [2,2] this order: 0,0; 0,1, 1,0; 1,1):

Array arr = ... // input array
for (int i = 0; i < arr.Length; i++)
{
     arr.SetValue(stream.ReadNextObject(), ???); // convert i -> int[] indexes
}

I know that the conversation of i can be done with % operator but it's hard to imagine an exact algorithm for multiple dimensions. There is only an answer for two-dimensions: Converting index of one dimensional array into two dimensional array i. e. row and column

I could use Stack<int> to store indexes while walking an array but it seems that % will be more efficient (I really need to care about performance here). But I'm not sure about Stack<T> vs %.

8
  • What are you trying to achieve? You want to read objects from a stream and store them in an array? i is already going over every possible index in your array, why don't you use arr.SetValue(stream.ReadNextObject(), i);? Commented Mar 7, 2016 at 15:09
  • 2
    @AudricdeSchaetzen that only works for 1 dimensional array Commented Mar 7, 2016 at 15:10
  • Sounds like switch/case. Why array is of unknown dimensions? How and from where you get it? Commented Mar 7, 2016 at 15:10
  • @Sinatr it's a serializer library. An array is created using reflection. Commented Mar 7, 2016 at 15:11
  • You can use recursion (because each dimension is a nested loop), check if item is of enumerable type (not sure if IEnumerable is the most basic) and if it is - then run same method recursively for this element. This will work for jagged array List<List<List<...>>>. Not sure what to do with multidimensional arrays though. Commented Mar 7, 2016 at 15:14

2 Answers 2

5

Here's the algorithm that I believe you are looking for

public static int[] SingleIndexToMulti(int index, int[] dimentionSizes)
{
    var result = new int[dimentionSizes.Length];
    for (int i = dimentionSizes.Length - 1; i >=0; i--)
    {
        result[i] = index % dimentionSizes[i];
        index = index / dimentionSizes[i];
    }

    return result;
}

You would use it like this

Array myArray = Whatever();
int[] dimensionSizes = new int[myArray.Rank];
for(int i =0; i < myArray.Rank; i++)
    dimensionsSizes[i] = myArray.GetLength(i);
for (int i = 0; i < arr.Length; i++)
{
    arr.SetValue(stream.ReadNextObject(), SingleIndexToMulti(i, dimensionSizes)); 
}

To demonstrate the following code

for(int i=0; i < (2*3*4) ;i++)
    Console.WriteLine(string.Join(",", SingleIndexToMulti(i, new[] { 2, 3, 4 })));

Produces

0,0,0

0,0,1

0,0,2

0,0,3

0,1,0

0,1,1

0,1,2

0,1,3

0,2,0

0,2,1

0,2,2

0,2,3

1,0,0

1,0,1

1,0,2

1,0,3

1,1,0

1,1,1

1,1,2

1,1,3

1,2,0

1,2,1

1,2,2

1,2,3

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

1 Comment

Yes, this is the algorithm I was looking for so I've accepted it. Until you posted this I couldn't even compare it with stack approach but now I see that using stack should be more efficient so I've chosen that way.
0

I have a recursive solution for you:

void Fill(Array a, Stream stream)
{
    int[] indices = new int[a.Rank]; // c# should initialize this with 0s
    FillRecursive(a, indices, 0, stream)    
}
void FillRecursive(Array a, int[] indices, int rank, Stream stream)
{
    for (int idx = 0; idx < a.GetLength(rank); idx++)
    {
        indices[rank] = idx;
        if (rank == a.Rank - 1)
            a.SetValue(stream.ReadNextObject(), indices);
        else
            FillRecursive(a, indices, rank + 1, stream);
    }
}

This should do the trick. I count up the individual dimensions and continously set the value. Note that Stream is kind of pseudo code (my Stream does not have a ReadNextObject).

I admit that I had no time to really test it. And I leave the performance measurements up to you, but I don't think there is a much faster alternative.

UPDATE: Test it, and it fills the "cells" in the correct order as you expected it. I testet it using an int[3,3,3] and a ReadNextValue that returns increasing integers. Result:

[0,0,0] => 0
[0,0,1] => 1
[0,0,2] => 2
[0,1,0] => 3
[0,1,1] => 4
...
[2,2,0] => 24
[2,2,1] => 25
[2,2,2] => 26

Note that this works not only for arbitrary dimensions, but also for arbitrary and different lengths of the individual dimensions.

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.