views:

314

answers:

6

If I've got a 2 dimensional array.

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

And I'd like to foreach through it like this.

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, i.e. iterate through the first dimension of the array with the iterator variable returning me the 1 dimensional array for that row?

Thanks.

+2  A: 
string[][] table = { ... };
Toby
+10  A: 

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]);
}
Marcelo Cantos
A small point: multidimensional arrays *are* enumerable, just not in the way the OP wants.
Odrade
Ok, @Odrade, I'll grant you that.
Marcelo Cantos
+3  A: 

If you define your array like this:

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

they you can use foreach loop on it.

Radoslav Hristov
...because then it's an array of an array. That also means your arrays could be jagged.
Nelson
Thanks, John ;)
Radoslav Hristov
+6  A: 

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[]>. 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). Then your static/extension method could return an IEnumerable<IList<T>> and provide deferred execution.

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

Dan Tao
+1 cause it's friday and crazy is totally ok :)
Isak Savo
A: 

Remember that a multi-dimensional array is like a table. You don't have an x element and a y element for each entry; you have a string at (for instance) 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. So, to get both entries at table[1, x], you'd do a nested for loop. 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]); } }

AllenG
+4  A: 

As others have suggested, you could use nested for-loops or redeclare your multidimensional array as a jagged one.

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
*/
Odrade