简体   繁体   English

如何通过二维数组“foreach”?

[英]How do I 'foreach' through a two-dimensional array?

I've got a two-dimensional array,我有一个二维数组,

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

And I'd like to foreach through it like this,我想像这样foreach它,

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

But, I get the error:但是,我收到错误:

Can't convert type string to string[]无法将类型字符串转换为字符串[]

Is there a way I can achieve what I want, ie iterate through the first dimension of the array with the iterator variable returning me the one-dimensional array for that row?有没有办法可以实现我想要的,即迭代数组的第一维,迭代器变量返回该行的一维数组?

Multidimensional arrays aren't enumerable.多维数组不可枚举。 Just iterate the good old-fashioned way:只需迭代好的老式方法:

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

As others have suggested, you could use nested for-loops or redeclare your multidimensional array as a jagged one.正如其他人所建议的那样,您可以使用嵌套的 for 循环或将多维数组重新声明为锯齿状数组。

However, I think it's worth pointing out that multidimensional arrays are enumerable, just not in the way that you want.但是,我认为值得指出的是多维数组可枚举的,只是不是您想要的方式。 For example:例如:

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

foreach (string s in table)
{
    Console.WriteLine(s);
}

/* Output is:
  aa
  aaa
  bb
  bbb
*/

If you define your array like this:如果您像这样定义数组:

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

Then you can use a foreach loop on it.然后您可以在其上使用 foreach 循环。

UPDATE : I had some time on my hands, so ... I went ahead and fleshed out this idea.更新:我有一些时间在我的手上,所以......我继续充实了这个想法。 See below for the code.请参阅下面的代码。


Here's a bit of a crazy answer:这是一个有点疯狂的答案:

You could do what you're looking for -- essentially treat a two-dimensional array as a table with rows -- by writing a static method (perhaps an extension method) that takes a T[,] and returns an IEnumerable<T[]> .可以做你正在寻找的东西——本质上把一个二维数组当作一个带有行的表——通过编写一个静态方法(可能是一个扩展方法),它接受一个T[,]并返回一个IEnumerable<T[]> This would require copying each "row" of the underlying table into a new array, though.不过,这需要将基础表的每一“行”复制到一个新数组中。

A perhaps better (though more involved) approach would be to actually write a class that implements IList<T> as a wrapper around a single "row" of a two-dimensional array (you would probably set IsReadOnly to true and just implement the getter for the this[int] property and probably Count and GetEnumerator ; everything else could throw a NotSupportedException ).一种可能更好(虽然更复杂)的方法是实际编写一个实现IList<T>作为二维数组的单个“行”的包装器(您可能将IsReadOnly设置为 true 并只实现 getter对于this[int]属性,可能还有CountGetEnumerator ;其他一切都可能抛出NotSupportedException )。 Then your static/extension method could return an IEnumerable<IList<T>> and provide deferred execution.然后您的静态/扩展方法可以返回一个IEnumerable<IList<T>>并提供延迟执行。

That way you could write code pretty much like what you have:这样你就可以像你所拥有的那样编写代码:

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

Just a thought.只是一个想法。


Implementation suggestion:实施建议:

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
    }
}

...now I think I should give StackOverflow a break for the rest of the day ;) ...现在我想我应该让 StackOverflow 休息一整天;)

It depends on how you define your multi-dimensional array.这取决于您如何定义多维数组。 Here are two options:这里有两个选项:

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

Here's a simple extension method that returns each row as an IEnumerable<T> .这是一个简单的扩展方法,它将每一行作为IEnumerable<T> This has the advantage of not using any extra memory:这具有不使用任何额外内存的优点:

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];
    }
}

Sample usage:示例用法:

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

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

Using LINQ you can do it like this:使用 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) {
    // …
}

This way it is also possible to use the foreach for looping through the columns instead of the rows by using Index2 in the GroupBy instead of Index 1. If you don't know the dimension of your array then you have to use the GetLength() method to determine the dimension and use that value in the quotient.通过这种方式,也可以使用 foreach 循环遍历列而不是行,方法是在 GroupBy 中使用 Index2 而不是 Index 1。如果您不知道数组的维度,则必须使用 GetLength()方法来确定维度并在商中使用该值。

I'm not a big fan of this method because of the memory usage involved, but if you use the arrays it produces, it isn't such a waste.由于涉及内存使用,我不是这种方法的忠实拥护者,但是如果您使用它生成的数组,则不会如此浪费。

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);
    }
}

Implementation:执行:

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]);
    }
});

The other solution I found is less memory intensive, but will use more CPU, especially when the dimensions of the arrays' entries are larger.我发现的另一个解决方案是内存密集度较低,但会使用更多的 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;
}

Implementation:执行:

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;
    }
});

As a whole, I find the first option to be more intuitive, especially if you want to access the produced array by its indexer.总的来说,我发现第一个选项更直观,特别是如果您想通过其索引器访问生成的数组。

At the end of the day, this is all just eye candy, neither methods should really be used in favour of directly accessing the source array;归根结底,这只是眼花缭乱,这两种方法都不应该用于直接访问源数组;

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]);
    }
}

Remember that a multi-dimensional array is like a table.请记住,多维数组就像一张表格。 You don't have an x element and a y element for each entry;每个条目都没有x元素和y元素; you have a string at (for instance) table[1,2] .您在(例如) table[1,2]处有一个字符串。

So, each entry is still only one string (in your example), it's just an entry at a specific x/y value.因此,每个条目仍然只是一个字符串(在您的示例中),它只是特定 x/y 值的条目。 So, to get both entries at table[1, x] , you'd do a nested for loop.因此,要获取table[1, x]两个条目,您需要执行嵌套的 for 循环。 Something like the following (not tested, but should be close)类似于以下内容(未测试,但应该接近)

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]);
    }
}
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);
                }
            }

I try this.我试试这个。 I hope to help.我希望有所帮助。 It work with它与

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;
            }
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM