首页 文章

我如何'foreach'通过二维数组?

提问于
浏览
38

我有一个二维数组,

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

而且我想像这样通过它,

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

但是,我得到错误:

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

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

12 回答

  • 2

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

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

    然后你可以在它上面使用foreach循环 .

  • 15

    这取决于您如何定义多维数组 . 这有两个选择:

    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();
            }
        }
    }
    
  • 23

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

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

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

    请记住,多维数组就像一个表 . 每个条目都没有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]);
        }
    }
    
  • 1

    多维数组不可枚举 . 只需迭代这种老式的方式:

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

    我试试这个 . 我希望能帮到你 . 它适用于

    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;
                }
            }
    
  • 0
    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);
                    }
                }
    
  • 4

    由于涉及内存使用,我不是这种方法的忠实粉丝,但是如果你使用它产生的数组,那就不是浪费了 .

    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]);
        }
    }
    
  • 2
    string[][] table = { ... };
    
  • 0

    使用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) {
        // …
    }
    

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

  • 23

    UPDATE :我有一些时间在我手上,所以......我继续前进并充实了这个想法 . 请参阅下面的代码 .


    这是一个疯狂的答案:

    你可以做你正在寻找的东西 - 基本上将二维数组视为一个带有行的表 - 通过编写一个静态方法(可能是一个扩展方法),它采用 T[,] 并返回一个 IEnumerable<T[]> . 但是,这需要将基础表的每个"row"复制到一个新数组中 .

    一个或许更好(虽然更复杂)的方法是实际编写一个实现 IList<T> 的类作为二维数组的单个"row"的包装器(您可能将 IsReadOnly 设置为true并且只为 this[int] 属性实现getter并且可能 CountGetEnumerator ;其他一切都可以抛出 NotSupportedException ) . 然后,您的静态/扩展方法可以返回 IEnumerable<IList<T>> 并提供延迟执行 .

    这样你就可以像编写的那样编写代码:

    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一个休息时间;)

  • 52

    这是一个简单的扩展方法,它将每一行作为 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) + "}");
    }
    

相关问题