使用嵌套的Foreach语句迭代多维数组


79

我认为这可能是一个非常简单的问题,但我还无法弄清楚。如果我有一个像这样的二维数组:

int[,] array = new int[2,3] { {1, 2, 3}, {4, 5, 6} };

用嵌套的foreach语句遍历数组的每个维度的最佳方法是什么?


它必须是二维数组,还是可以使用数组数组?
马修·弗拉申

Answers:


130

如果要遍历数组中的每个项目,就好像它是一个扁平数组一样,则可以执行以下操作:

foreach (int i in array) {
    Console.Write(i);
}

将打印

123456

如果您还希望知道x和y索引,则需要执行以下操作:

for (int x = 0; x < array.GetLength(0); x += 1) {
    for (int y = 0; y < array.GetLength(1); y += 1) {
        Console.Write(array[x, y]);
    }
}

或者,您可以改用锯齿状的数组(数组的数组):

int[][] array = new int[2][] { new int[3] {1, 2, 3}, new int[3] {4, 5, 6} };
foreach (int[] subArray in array) {
    foreach (int i in subArray) {
        Console.Write(i);
    }
}

要么

int[][] array = new int[2][] { new int[3] {1, 2, 3}, new int[3] {4, 5, 6} };
for (int j = 0; j < array.Length; j += 1) {
    for (int k = 0; k < array[j].Length; k += 1) {
        Console.Write(array[j][k]);
    }
}

1
在看到我的方法的错误(例如,尝试使用像锯齿状数组的2D数组)后,我同意迭代该列表的最佳方法就像在第一个示例中一样。您之所以投票,是因为您愿意超越答案并给出其他选择。
泰勒·莫里

为什么2维数组变平?

C#=它总是可以正常工作。在浪费一些时间之前,C ++ =无效。
jw_


6

对于多维数组,可以使用相同的方法遍历元素,例如:

int[,] numbers2D = new int[3, 2] { { 9, 99 }, { 3, 33 }, { 5, 55 } };
foreach (int i in numbers2D)
{
    System.Console.Write("{0} ", i);
}

该示例的输出为:

9 99 3 33 5 55

参考文献


在Java中,多维数组是数组的数组,因此以下工作原理:

    int[][] table = {
            { 1, 2, 3 },
            { 4, 5, 6 },
    };
    for (int[] row : table) {
        for (int el : row) {
            System.out.println(el);
        }
    }

3
C#具有多维和锯齿状数组作为单独的概念,其中int[,]是二维数组,并且int[][]是数组的锯齿状数组,并且每个给定的数组都不需要具有相同的长度。您可以轻松地在锯齿形数组上执行foreach,但是2D数组的结构类型不同。无论如何,您的第二个片段不适合这个问题,第一个片段没有嵌套。
安东尼·佩格拉姆

4

我知道这是一篇旧文章,但是我是通过Google找到的,并且在玩了之后认为我有一个更简单的解决方案。如果我错了,请指出,“因为我想知道,但这至少对我有用(基于ICR的回复):

for (int x = 0; x < array.GetLength(0); x++)
{
    Console.Write(array[x, 0], array[x,1], array[x,2]);
}

由于这两个维度都是有限的,所以任何一个都可以是简单的数字,从而避免嵌套的for循环。我承认我是C#的新手,所以请,如果有理由不这样做,请告诉我...


1
这是假设第二维的长度。您正在硬编码第二维将始终具有长度3的假设,事实可能是这样,也可能不是。如果不是3,而是50,那么您将要进行大量复制/粘贴,并使代码不可读。:)
亚当·李尔

@AnnaLear假设不一定是错误的,您可能正在处理长度明确定义的矩阵,例如15x15拼字板
Jaycee

3

C#中的2D数组不适用于嵌套的foreach,它不等于锯齿状数组(数组的数组)。你可以做这样的事来使用foreach

foreach (int i in Enumerable.Range(0, array.GetLength(0)))
    foreach (int j in Enumerable.Range(0, array.GetLength(1)))
        Console.WriteLine(array[i, j]);

但是您仍将i和j用作数组的索引值。如果只是for改用花园品种循环,则会更好地保留可读性。


1
这是经典,简单和易于理解的结构的可怕突变。如果您的代码包含此内容,那么您确实需要思考“嘿,这行得通,但是……怎么不行?” (我很清楚这在python家族中很常见。但是-这是C#。)
Rubys,2010年

@Rubys,这在Python中也不是习惯用法。获取索引然后使用来访问列表是非常不合常规的[]。直接遍历值是惯用的Python。另外请注意,Python中没有多维数组或列表(只有锯齿状)。
马修·弗拉申

@Matthew:我可能已经将python与另一种语言混淆了,我真的不知道我在脚本家族中的胳膊(我想这就是我在python系列中的意思)
Rubys,2010年

2

两种方式:

  1. 将数组定义为锯齿状数组,并使用嵌套的foreach。
  2. 通常定义数组,并对整个对象使用foreach。

#2的示例:

int[,] arr = { { 1, 2 }, { 3, 4 } };
foreach(int a in arr)
    Console.Write(a);

输出将是1234。与i从0到n,j从0到n完全相同。


2

您可以使用如下扩展方法:

internal static class ArrayExt
{
    public static IEnumerable<int> Indices(this Array array, int dimension)
    {
        for (var i = array.GetLowerBound(dimension); i <= array.GetUpperBound(dimension); i++)
        {
            yield return i;
        }
    }
}

然后:

int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } };
foreach (var i in array.Indices(0))
{
    foreach (var j in array.Indices(1))
    {
        Console.Write(array[i, j]);
    }

    Console.WriteLine();
}

它比使用for循环要慢一些,但在大多数情况下可能不是问题。不知道它是否使事情更具可读性。

请注意,c#数组可以不是基于零的数组,因此可以使用如下所示的for循环:

int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } };
for (var i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
{
    for (var j= array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
    {
        Console.Write(array[i, j]);
    }

    Console.WriteLine();
}

很奇怪...什么不是基于零的?
drzaus

我想我在与Excel互操作时看到了基于非零的值,这并不常见,但可能需要注意。
约翰·拉尔森

1

就像在其他地方提到的那样,您可以遍历数组,它将按维度生成所有结果。但是,如果您也想知道索引,那么如何使用它-http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with- linq.aspx

然后做类似的事情:

var dimensionLengthRanges = Enumerable.Range(0, myArray.Rank).Select(x => Enumerable.Range(0, myArray.GetLength(x)));
var indicesCombinations = dimensionLengthRanges.CartesianProduct();

foreach (var indices in indicesCombinations)
{
    Console.WriteLine("[{0}] = {1}", string.Join(",", indices), myArray.GetValue(indices.ToArray()));
}

1

使用LINQ.Cast<int>()将2D数组转换为IEnumerable<int>

LINQPad示例:

var arr = new int[,] { 
  { 1, 2, 3 }, 
  { 4, 5, 6 } 
};

IEnumerable<int> values = arr.Cast<int>();
Console.WriteLine(values);

输出:

顺序是1,2,3,4,5,6


0

您也可以使用枚举器。任何维度的每种数组类型都支持Array.GetEnumerator方法。唯一的警告是您将不得不处理装箱/拆箱。但是,您需要编写的代码非常简单。

这是示例代码:

class Program
{
    static void Main(string[] args)
    {
        int[,] myArray = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
        var e = myArray.GetEnumerator();

        e.Reset();

        while (e.MoveNext())
        {
            // this will output each number from 1 to 6. 
            Console.WriteLine(e.Current.ToString());
        }

        Console.ReadLine();
    }
}

投票-1 ...我引用msdnUsing foreach is recommended, instead of directly manipulating the enumerator.
伊凡(Ivan)

0
int[,] arr =  { 
                {1, 2, 3},
                {4, 5, 6},
                {7, 8, 9}
              };
 for(int i = 0; i < arr.GetLength(0); i++){
      for (int j = 0; j < arr.GetLength(1); j++)
           Console.Write( "{0}\t",arr[i, j]);
      Console.WriteLine();
    }

output:  1  2  3
         4  5  6
         7  8  9

0

我一直在寻找一种解决方案,该方法枚举一个在编译时具有访问每个元素索引集的未知数组。我看到了具有收益的解决方案,但这是另一个没有收益的实现。它以旧派简约的方式。在此示例中,AppendArrayDebug()仅将所有元素打印到StringBuilder缓冲区中。

public static void AppendArrayDebug ( StringBuilder sb, Array array )
{
    if( array == null || array.Length == 0 )
    {
        sb.Append( "<nothing>" );
        return;
    }

    int i;

    var rank = array.Rank;
    var lastIndex = rank - 1;

    // Initialize indices and their boundaries
    var indices = new int[rank];
    var lower = new int[rank];
    var upper = new int[rank];
    for( i = 0; i < rank; ++i )
    {
        indices[i] = lower[i] = array.GetLowerBound( i );
        upper[i] = array.GetUpperBound( i );
    }

    while( true )
    {
        BeginMainLoop:

        // Begin work with an element

        var element = array.GetValue( indices );

        sb.AppendLine();
        sb.Append( '[' );
        for( i = 0; i < rank; ++i )
        {
            sb.Append( indices[i] );
            sb.Append( ' ' );
        }
        sb.Length -= 1;
        sb.Append( "] = " );
        sb.Append( element );

        // End work with the element

        // Increment index set

        // All indices except the first one are enumerated several times
        for( i = lastIndex; i > 0; )
        {
            if( ++indices[i] <= upper[i] )
                goto BeginMainLoop;
            indices[i] = lower[i];
            --i;
        }

        // Special case for the first index, it must be enumerated only once
        if( ++indices[0] > upper[0] )
            break;
    }
}

例如,以下数组将产生以下输出:

var array = new [,,]
{
    { {  1,  2,  3 }, {  4,  5,  6 }, {  7,  8,  9 }, { 10, 11, 12 } },
    { { 13, 14, 15 }, { 16, 17, 18 }, { 19, 20, 21 }, { 22, 23, 24 } }
};

/*
Output:

[0 0 0] = 1
[0 0 1] = 2
[0 0 2] = 3
[0 1 0] = 4
[0 1 1] = 5
[0 1 2] = 6
[0 2 0] = 7
[0 2 1] = 8
[0 2 2] = 9
[0 3 0] = 10
[0 3 1] = 11
[0 3 2] = 12
[1 0 0] = 13
[1 0 1] = 14
[1 0 2] = 15
[1 1 0] = 16
[1 1 1] = 17
[1 1 2] = 18
[1 2 0] = 19
[1 2 1] = 20
[1 2 2] = 21
[1 3 0] = 22
[1 3 1] = 23
[1 3 2] = 24
*/
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.