【问题标题】:How do I 'foreach' through a two-dimensional array?我如何通过二维数组“foreach”?
【发布时间】:2011-02-19 12:36:36
【问题描述】:

我有一个二维数组,

string[,] table = {
                       { "aa", "aaa" },
                       { "bb", "bbb" }
                   };

我想像这样通过它foreach

foreach (string[] row in table)
{
    Console.WriteLine(row[0] + " " + row[1]);
}

但是,我得到了错误:

无法将类型字符串转换为字符串[]

有没有一种方法可以实现我想要的,即使用迭代器变量迭代数组的第一个维度,返回该行的一维数组?

【问题讨论】:

  • 提示:string[,] != string[][]

标签: c# arrays multidimensional-array


【解决方案1】:

如果你这样定义你的数组:

string[][] table = new string[][] {
                       new string[] { "aa", "aaa" },
                       new string[]{ "bb", "bbb" }
};

然后你可以在它上面使用一个foreach循环。

【讨论】:

  • ...因为它是一个数组的数组。这也意味着您的数组可能是锯齿状的。
  • 这不是二维数组。它是数组中的数组。这与使用普通数组一样有效,在一个具有 X 和 Y 参数的 getter 和 setter 并使用 x*width+y 公式的类中。
  • 不要使用foreach - 使用嵌套的for 循环,数组的每个维度一个。
  • foreach + 错误方式没有解释或示例
【解决方案2】:

这是一个简单的扩展方法,它以IEnumerable<T> 的形式返回每一行。这样做的好处是不使用任何额外的内存:

public static class Array2dExt
{
    public static IEnumerable<IEnumerable<T>> Rows<T>(this T[,] array)
    {
        for (int r = array.GetLowerBound(0); r <= array.GetUpperBound(0); ++r)
            yield return row(array, r);
    }

    static IEnumerable<T> row<T>(T[,] array, int r)
    {
        for (int c = array.GetLowerBound(1); c <= array.GetUpperBound(1); ++c)
            yield return array[r, c];
    }
}

示例用法:

static void Main()
{
    string[,] siblings = { { "Mike", "Amy" }, { "Mary", "Albert" }, {"Fred", "Harry"} };

    foreach (var row in siblings.Rows())
        Console.WriteLine("{" + string.Join(", ", row) + "}");
}

【讨论】:

    【解决方案3】:

    由于涉及到内存使用,我不是这种方法的忠实拥护者,但如果您使用它产生的数组,那就不是浪费了。

    public static void ForEachRow<T>(this T[,] list, Action<int, T[]> action)
    {
        var len = list.GetLength(0);
        var sub = list.GetLength(1);
    
        T[] e;
        int i, j;
    
        for (i = 0; i < len; i++)
        {
            e = new T[sub];
    
            for (j = 0; j < sub; j++)
            {
                e[j] = list[i, j];
            }
    
            action(i, e);
        }
    }
    

    实施:

    var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};
    
    list.ForEachRow((i, row) =>
    {
        for (var j = 0; j < row.Length; j++)
        {
            Console.WriteLine("[{0},{1}]: {2}", i, j, row[j]);
        }
    });
    

    我发现的另一个解决方案是内存占用较少,但会使用更多 CPU,尤其是当数组条目的维度较大时。

    public static void ForEachRow<T>(this T[,] list, Action<int, IEnumerable<T>> action)
    {
        var len = list.GetLength(0);
        var sub = list.GetLength(1);
    
        int i, j;
        IEnumerable<T> e;
    
        for (i = 0; i < len; i++)
        {
            e = Enumerable.Empty<T>();
    
            for (j = 0; j < sub; j++)
            {
                e = e.Concat(AsEnumerable(list[i, j]));
            }
    
            action(i, e);
        }
    }
    
    private static IEnumerable<T> AsEnumerable<T>(T add)
    {
        yield return add;
    }
    

    实施:

    var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};
    
    list.ForEachRow((i, row) =>
    {
        var j = 0;
    
        forrach (var o in row)
        {
            Console.WriteLine("[{0},{1}]: {2}", i, j, o);
    
            ++j;
        }
    });
    

    总的来说,我发现第一个选项更直观,特别是如果您想通过其索引器访问生成的数组。

    归根结底,这一切都只是锦上添花,这两种方法都不应该真正用于支持直接访问源数组;

    for (var i = 0; i < list.GetLength(0); i++)
    {
        foreach (var j = 0; j < list.GetLength(1); j++)
        {
            Console.WriteLine("[{0},{1}]: {2}", i, j, list[i, j]);
        }
    }
    

    【讨论】:

      【解决方案4】:

      这取决于您如何定义多维数组。这里有两个选项:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      
      namespace ConsoleApplication2
      {
          class Program
          {
              static void Main(string[] args)
              {
                  // First
                  string[,] arr1 = {
                             { "aa", "aaa" },
                             { "bb", "bbb" }
                         };
      
                  // Second
                  string[][] arr2 = new[] {
                      new[] { "aa", "aaa" },
                      new[] { "bb", "bbb" }
                  };
      
                  // Iterate through first
                  for (int x = 0; x <= arr1.GetUpperBound(0); x++)
                      for (int y = 0; y <= arr1.GetUpperBound(1); y++)
                          Console.Write(arr1[x, y] + "; ");
      
                  Console.WriteLine(Environment.NewLine);
      
                  // Iterate through second second
                  foreach (string[] entry in arr2)
                      foreach (string element in entry)
                          Console.Write(element + "; ");
      
                  Console.WriteLine(Environment.NewLine);
                  Console.WriteLine("Press any key to finish");
                  Console.ReadKey();
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        使用 LINQ 你可以这样做:

        var table_enum = table
        
            // Convert to IEnumerable<string>
            .OfType<string>()
        
            // Create anonymous type where Index1 and Index2
            // reflect the indices of the 2-dim. array
            .Select((_string, _index) => new {
                Index1 = (_index / 2),
                Index2 = (_index % 2), // ← I added this only for completeness
                Value = _string
            })
        
            // Group by Index1, which generates IEnmurable<string> for all Index1 values
            .GroupBy(v => v.Index1)
        
            // Convert all Groups of anonymous type to String-Arrays
            .Select(group => group.Select(v => v.Value).ToArray());
        
        // Now you can use the foreach-Loop as you planned
        foreach(string[] str_arr in table_enum) {
            // …
        }
        

        这样,也可以使用 foreach 循环遍历列而不是行,方法是在 GroupBy 中使用 Index2 而不是 Index 1。如果您不知道数组的维度,则必须使用GetLength() 方法来确定维度并在商中使用该值。

        【讨论】:

          【解决方案6】:

          请记住,多维数组就像一张表。每个条目都没有 x 元素和 y 元素;你有一个字符串(例如)table[1,2]

          因此,每个条目仍然只是一个字符串(在您的示例中),它只是一个特定 x/y 值的条目。因此,要获得table[1, x] 的两个条目,您需要执行嵌套的 for 循环。类似于以下内容(未经测试,但应该很接近)

          for (int x = 0; x < table.Length; x++)
          {
              for (int y = 0; y < table.Length; y += 2)
              {
                  Console.WriteLine("{0} {1}", table[x, y], table[x, y + 1]);
              }
          }
          

          【讨论】:

            【解决方案7】:

            我试试这个。我希望能有所帮助。它适用于

            static void Main()
                {
                    string[,] matrix = {
                                           { "aa", "aaa" },
                                           { "bb", "bbb" }
                                       };
                    int index = 0;
                    foreach (string element in matrix)
                    {
                        if (index < matrix.GetLength(1))
                        {
                            Console.Write(element);
                            if (index < (matrix.GetLength(1) - 1))
                            {
                                Console.Write(" ");
                            }
                            index++;
                        }
                        if (index == matrix.GetLength(1))
                        {
                            Console.Write("\n");
                            index = 0;
                        }
                    }
            

            【讨论】:

              【解决方案8】:
              string[][] languages = new string[2][];
                          languages[0] = new string[2];
                          languages[1] = new string[3];
              // inserting data into double dimensional arrays.
                          for (int i = 0; i < 2; i++)
                          {
                              languages[0][i] = "Jagged"+i.ToString();
                          }
                          for (int j = 0; j < 3; j++)
                          {
                              languages[1][j] = "Jag"+j.ToString();
                          }
              
              // doing foreach through 2 dimensional arrays.
              foreach (string[] s in languages)
                          {
                              foreach (string a in s)
                              {
                                  Console.WriteLine(a);
                              }
                          }
              

              【讨论】:

                【解决方案9】:

                更新:我手头有一些时间,所以......我继续充实了这个想法。代码见下文。


                这是一个有点疯狂的答案:

                可以做您正在寻找的事情——本质上将二维数组视为具有行的表——通过编写一个采用@的静态方法(可能是扩展方法) 987654321@ 并返回 IEnumerable&lt;T[]&gt;。不过,这需要将基础表的每一“行”复制到一个新数组中。

                一种可能更好(尽管涉及更多)的方法是实际编写一个实现 IList&lt;T&gt; 的类作为二维数组的单个“行”的包装器(您可能会将 IsReadOnly 设置为 true 并且只需为this[int] 属性实现getter,可能还有CountGetEnumerator;其他一切都可以抛出NotSupportedException)。然后您的静态/扩展方法可以返回 IEnumerable&lt;IList&lt;T&gt;&gt; 并提供延迟执行。

                这样您就可以编写与您所拥有的代码非常相似的代码:

                foreach (IList<string> row in table.GetRows()) // or something
                {
                    Console.WriteLine(row[0] + " " + row[1]);
                }
                

                只是一个想法。


                实施建议:

                public static class ArrayTableHelper {
                    public static IEnumerable<IList<T>> GetRows<T>(this T[,] table) {
                        for (int i = 0; i < table.GetLength(0); ++i)
                            yield return new ArrayTableRow<T>(table, i);
                    }
                
                    private class ArrayTableRow<T> : IList<T> {
                        private readonly T[,] _table;
                        private readonly int _count;
                        private readonly int _rowIndex;
                
                        public ArrayTableRow(T[,] table, int rowIndex) {
                            if (table == null)
                                throw new ArgumentNullException("table");
                
                            if (rowIndex < 0 || rowIndex >= table.GetLength(0))
                                throw new ArgumentOutOfRangeException("rowIndex");
                
                            _table = table;
                            _count = _table.GetLength(1);
                            _rowIndex = rowIndex;
                        }
                
                        // I didn't implement the setter below,
                        // but you easily COULD (and then set IsReadOnly to false?)
                        public T this[int index] {
                            get { return _table[_rowIndex, index]; }
                            set { throw new NotImplementedException(); }
                        }
                
                        public int Count {
                            get { return _count; }
                        }
                
                        bool ICollection<T>.IsReadOnly {
                            get { return true; }
                        }
                
                        public IEnumerator<T> GetEnumerator() {
                            for (int i = 0; i < _count; ++i)
                                yield return this[i];
                        }
                
                        // omitted remaining IList<T> members for brevity;
                        // you actually could implement IndexOf, Contains, etc.
                        // quite easily, though
                    }
                }
                

                ...现在我想我应该让 StackOverflow 休息一下;)

                【讨论】:

                  【解决方案10】:

                  正如其他人所建议的,您可以使用嵌套的 for 循环或将多维数组重新声明为锯齿状数组。

                  但是,我认为值得指出的是,多维数组可枚举的,只是不是以您想要的方式。例如:

                  string[,] table = {
                                        { "aa", "aaa" },
                                        { "bb", "bbb" }
                                    };
                  
                  foreach (string s in table)
                  {
                      Console.WriteLine(s);
                  }
                  
                  /* Output is:
                    aa
                    aaa
                    bb
                    bbb
                  */
                  

                  【讨论】:

                    【解决方案11】:

                    多维数组不可枚举。只需迭代好的老式方法:

                    for (int i = 0; i < table.GetLength(0); i++)
                    {
                        Console.WriteLine(table[i, 0] + " " + table[i, 1]);
                    }
                    

                    【讨论】:

                    • 一个小点:多维数组可枚举的,只是不是OP想要的方式。
                    • 好的,@Odrade,我会同意的。
                    • 实际上做了 OP 想要的,因此是最好的答案。 (虽然只是在“做我想做的,而不是我要求的”意义上!)
                    【解决方案12】:
                    string[][] table = { ... };
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-04-07
                      • 2013-06-01
                      • 2018-12-28
                      • 2018-06-25
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-08-15
                      • 1970-01-01
                      相关资源
                      最近更新 更多