开发者

How to iterate over 3D String array in C#

开发者 https://www.devze.com 2023-01-14 13:43 出处:网络
I have a 3D array String[][,] cross = {new String[,]{{\"1\", \"b\", \"b\", \"b\"}, {\"b\", \"c\", \"c\", \"c\"}},new String[,]{{\"2\", \"b\", \"b\", \"e\"}, {\"b\", \"c\", \"c\", \"d\"}}}

I have a 3D array

String[][,] cross = {new String[,]{{"1", "b", "b", "b"}, {"b", "c", "c", "c"}},new String[,]{{"2", "b", "b", "e"}, {"b", "c", "c", "d"}}}

How to iterate over this array.

I want to iterate like this

foreach(String[,] abc in cross) //abc must be the开发者_StackOverflow中文版 first/second 2D array
  foreach(string[] arr in abc) //arr must hold {"1", "b", "b", "b"} (say)
  {
  }

I tried this, but not working.


you need 3 levels of nested for loops, one for each dimension


This should work:

foreach (string s in cross.SelectMany(x => x.Cast<string>()))
{
    // Code goes here.
}

UPDATE: Based on your comment, it looks like you want for your enumeration to at some point deal with a string[] that looks like this:

{"1", "b", "b", "b"}

The problem is: no such array exists in the array you've declared. This can be confusing because there is overlap between the syntax used to declare a T[] array and that used to declare a T[,] array.

Let's write out your initialization to make it clearer:

string[][,] cross = {
    new string[,] {
        {"1", "b", "b", "b"},
        {"b", "c", "c", "c"}
    },
    new string[,] {
        {"2", "b", "b", "e"},
        {"b", "c", "c", "d"}
    }
};

What we have here is two string[,] arrays of dimensions 4x2. The expression {"1", "b", "b", "b"} above does not represent an individual array, but rather the values in one dimension of your multidimensional array.

To achieve the behavior you seem to want, Mark Cidade's answer is right on the money: you can't do it with a string[][,], but you can do it with a string[][][].

string[][][] cross = new[] {
    new[] {
        new[] {"1", "b", "b", "b"},
        new[] {"b", "c", "c", "c"}
    },
    new[] {
        new[] {"2", "b", "b", "e"},
        new[] {"b", "c", "c", "d"}
    }
};

Declaring cross in the above way allows you to do the following:

foreach (string[][] abc in cross)
{
    foreach (string[] arr in abc)
    {
        Console.WriteLine(string.Join(", ", arr));
    }
}

Or, to borrow from my original suggestion:

foreach (string[] arr in cross.SelectMany(x => x))
{
    Console.WriteLine(string.Join(", ", arr));
}

Output:

1, b, b, b
b, c, c, c
2, b, b, e
b, c, c, d


Given your jagged array of 2D arrays, you would perform classic iteration like the following

foreach (string[,] array in cross)
{
   for (int i = 0; i < array.GetLength(0); i++)
   {
       for (int j = 0; j < array.GetLength(1); j++)
       {
           string item = array[i, j];
           // do something with item
       }
   }
}


A string[,] doesn't work the same way as a string[][]—it's a square array, not an array of arrays. When you use it in a foreach statement, the enumerator will give you a sequence of individual strings, similar to the following:

foreach(string[,] abc in cross)
 for(int i=0; i < abc.GetLength(0); ++i)
   for(int j=0; j < abc.GetLength(1); ++j)
    { string str = abc[i,j];
    }

If you want something similar to your iteration code, then you want a string[][][] instead of a string[][,]:

string[][][] cross = { new string[][]{new string[]{"1", "b", "b", "b"}, new  string[]{"b", "c", "c", "c"}}
                      ,new string[][]{new string[]{"2", "b", "b", "e"}, new string[]{"b", "c", "c", "d"}}};

foreach(string[][] abc in cross)
  foreach(string[] arr in abc)
   { 
   }


3D array should look like this in my way:

string[, ,] arr = new string[,,]{
    {
        {"a1", "b1", "c1"},
        {"a2", "b2", "c2"},
        {"a3", "b3", "c3"},
    },{
        {"a4", "b4", "c4"},
        {"a5", "b5", "c5"},
        {"a6", "b6", "c6"},
    }
};

and iterating through all items one by one can be done in this way:

for (int i = 0; i < arr.GetLength(0); i++)
{
    for (int j = 0; j < arr.GetLength(1); j++)
    {
        for (int k = 0; k < arr.GetLength(2); k++)
        {
            string s = arr[i, j, k];
        }
    }
}


foreach(String[,] abc in cross)
  foreach(string s in abc) //removed [] from inner loop
  {
      // do something with s
  }
}


It sounds like what you want is to end up with is arrays containing the second elements of the 2D arrays, grouped by the first element. So given your example, you want results like this:

Iteration 1a: {"1", "b", "b", "b"}
Iteration 1b: {"b", "c", "c", "c"}
Iteration 2a: {"2", "b", "b", "e"}
Iteration 2b: {"b", "c", "c", "d"}

Understand that this cannot be achieved efficiently, because a 2D array is not stored as multiple arrays, but instead as a singular block of memory. In memory, your array will look this this:

"1", "b", "b", "b", "b", "c", "c", "c", "2", "b", "b", "e", "b", "c", "c", "d"

And when you access it with a[y, x], the correct element is chosen by y * a.GetLength(0) + x.

If you really want an array of arrays, then you should use [][] instead of [,], which is what others have suggested. If, on the other hand, you're stuck with the multidimensional array for other reasons, then you'll have to build or fake the inner arrays.

To build the inner arrays:

foreach(string[,] square in cross)
    for(int y = 0; y < square.GetUpperBound(0); y++){
        string[] inner = new string[square.GetLength(1)];
        for(int x = 0; x < inner.Length; x++)
            inner[x] = square[y, x];
        // now do something with inner
    }

That's pretty inefficient, though, so you're better off faking it. If you only need to iterate through it later, then you can create an enumerable:

foreach(string[,] square in cross)
    for(int y = 0; y < square.GetUpperBound(0); y++){
        var inner = GetEnumeratedInner(square, y);
        // now do something with inner
    }

...

static IEnumerable<string> GetEnumeratedInner(string[,] square, int y){
    for(int x = 0; x < square.GetUpperBound(1); x++)
        yield return square[y, x];
}

If you really need to access it by index, as you could with an array, then an indexed class will do the trick:

foreach(string[,] square in cross)
    for(int y = 0; y < square.GetUpperBound(0); y++){
        var inner = new IndexedInner(square, y);
        // now do something with inner
    }

...

// this class should really implement ICollection<T> and System.Collections.IList,
// but that would be too much unimportant code to put here
class IndexedInner<T> : IEnumerable<T>{
    T[,] square;
    int  y;

    public IndexedInner(T[,] square, int y){
        this.square = square;
        this.y      = y;
    }

    public int Length{get{return square.GetLength(1);}}

    public T this[int x]{
        get{return square[y, x];}
        set{square[y, x] = value;}
    }

    public IEnumerator<T> GetEnumerator(){
        for(int x = 0; x < square.GetUpperBound(1); x++)
            yield return square[y, x];
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){
        return GetEnumerator();
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消