foreach with parallel iterations of multiple properties

  • Thread starter Thread starter John A Grandy
  • Start date Start date
J

John A Grandy

Is it possible to write a foreach so that it simultaneously iterates through
two collections ( or two properties of the same collection ) ?

Following is *only meant as an example* :

-- iterate a dictionary obtaining the next key and value on each iteration.

The following doesn't work

foreach ( string key in MyDictionary.Keys , object value in
MyDictionary.Values )
{


.... but perhaps there's some other way ...
 
John said:
Is it possible to write a foreach so that it simultaneously iterates through
two collections ( or two properties of the same collection ) ?
No, because there's no meaningful definition of "simultaneous" (what should
happen if one of the iterations is done before the other one?)
Following is *only meant as an example* :

-- iterate a dictionary obtaining the next key and value on each iteration.

The following doesn't work

foreach ( string key in MyDictionary.Keys , object value in
MyDictionary.Values )
{


... but perhaps there's some other way ...
What type is "MyDictionary"? It should implement
IEnumerable<KeyValuePair<TKey, TValue>>, like IDictionary<TKey, TValue>
does. Then it's a matter of writing

foreach (KeyValuePair<string, object> p in MyDictionary) {
string key = p.Key;
object value = p.Value;
...
}

If it's one of the legacy collection types that has no intuitive IEnumerable
implementation, it's a simple matter of iterating over the keys only:

foreach (string key in MyDictionary.Keys) {
object value = MyDictionary[key];
...
}

Of course, since you specify that this is only "an example", you may not be
talking about dictionaries at all. It's always possible to use enumerators
directly for advanced scenarios:

using (IEnumerator<string> iKeys = MyDictionary.Keys.GetEnumerator()) {
using (IEnumerator<object> iObject = MyDictionary.Values.GetEnumerator()) {
// use .MoveNext() and .Current here as you please
}
}

But this is seldom necessary, and the resulting code isn't very readable.
 
Re making it readable... perhaps an extension method (or take of the "this"
in C# 2 to get a static helper method) to walk the two in parallel;
something like below; this could be used i.e.

collectionA.ForEach(collectionB, (a,b)=> Console.WriteLine("{0}/ {1}", a,b);

which isn't exactly unreadable...

Marc

public static void ForEach<TLhs, TRhs>(this IEnumerable<TLhs>
leftSequence,
IEnumerable<TRhs> rightSequence, Action<TLhs, TRhs> action)
{
if (leftSequence == null) throw new
ArgumentNullException("leftSequence");
if (rightSequence == null) throw new
ArgumentNullException("rightSequence");
if (leftSequence == null) throw new ArgumentNullException("action");
using (var lhs = leftSequence.GetEnumerator())
using (var rhs = rightSequence.GetEnumerator())
{
while (lhs.MoveNext() && rhs.MoveNext())
{
action(lhs.Current, rhs.Current);
}
}
}
 
If it's one of the legacy collection types that has no intuitive IEnumerable
implementation, it's a simple matter of iterating over the keys only:

   foreach (string key in MyDictionary.Keys) {
     object value = MyDictionary[key];
     ...
   }

Of course, since you specify that this is only "an example", you may not be
talking about dictionaries at all. It's always possible to use enumerators
directly for advanced scenarios:

   using (IEnumerator<string> iKeys = MyDictionary.Keys.GetEnumerator()) {
     using (IEnumerator<object> iObject = MyDictionary.Values.GetEnumerator()) {
       // use .MoveNext() and .Current here as you please
     }
   }

But this is seldom necessary, and the resulting code isn't very readable.

This is debateful; personally, I would very much prefer the second
approach you propose to the first, if only because the first does an
unnecessary key lookup for every key, which can be pretty costly in a
loop. If using a legacy collection (I assume you mean Hashtable), then
the proper approach is to obtain an IDictionaryEnumerator from it
explicitly, and use that.
 
Pavel said:
If it's one of the legacy collection types that has no intuitive IEnumerable
implementation, it's a simple matter of iterating over the keys only:

foreach (string key in MyDictionary.Keys) {
object value = MyDictionary[key];
...
}

Of course, since you specify that this is only "an example", you may not be
talking about dictionaries at all. It's always possible to use enumerators
directly for advanced scenarios:

using (IEnumerator<string> iKeys = MyDictionary.Keys.GetEnumerator()) {
using (IEnumerator<object> iObject = MyDictionary.Values.GetEnumerator()) {
// use .MoveNext() and .Current here as you please
}
}

But this is seldom necessary, and the resulting code isn't very readable.

This is debateful; personally, I would very much prefer the second
approach you propose to the first, if only because the first does an
unnecessary key lookup for every key, which can be pretty costly in a
loop.

Meh. If it's a dictionary type, the lookup time shouldn't matter except in
pathological cases. Getting an explicit enumerator for the values isn't
necessarily faster (I'll grant you that it probably isn't any *slower*,
though, since the implementation could always use the key-lookup method if
it couldn't think of anything better).
If using a legacy collection (I assume you mean Hashtable),

That's one of them. There are more custom collections in the framework than
you can shake a stick at, actually, some of which don't even implement
IEnumerable (but let's politely ignore those).
then the proper approach is to obtain an IDictionaryEnumerator from it
explicitly, and use that.

This works for Hashtable; it doesn't work for (say) NameObjectCollectionBase
and derivates, which return an enumerator over the keys only. If you *can*
obtain an enumerator that obtains the values as pairs, then yes, by all
means use that in preference to any of these approaches.
 
Re making it readable... perhaps an extension method (or take of the "this"
in C# 2 to get a static helper method) to walk the two in parallel;
something like below; this could be used i.e.

<snip>

FWIW, Eric Lippert mentioned that MS effectively "missed" an operator
for LINQ here - the "Zip" operator. who knows, it may even get
implemented in a future version.

Of course, there are options here in terms of what should happen when
the left or right sequence ends before the other one does - it could
through an exception, return pairs with the default value for the
type, or just end abruptly (as yours does).

Candidate for MiscUtil, perhaps?

Jon
 
<snip>

FWIW, Eric Lippert mentioned that MS effectively "missed" an operator
for LINQ here - the "Zip" operator. who knows, it may even get
implemented in a future version.

For a laugh, here's a LINQ 1-liner that seems to do that:

xs.Select((x, i) => new { x = x, i = i }).Join(ys.Select((y, i) => new
{ y = y, i = i }), xi => xi.i, yi => yi.i, (xi, yi) => new { xi.x,
yi.y })

Performance-wise, of course, this is so bad it's not even a contender.
 
Back
Top