Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does .NET have a built in IEnumerable for multiple collections?

I need an easy way to iterate over multiple collections without actually merging them, and I couldn't find anything built into .NET that looks like it does that. It feels like this should be a somewhat common situation. I don't want to reinvent the wheel. Is there anything built in that does something like this:

public class MultiCollectionEnumerable<T> : IEnumerable<T>
{
    private MultiCollectionEnumerator<T> enumerator;
    public MultiCollectionEnumerable(params IEnumerable<T>[] collections)
    {
        enumerator = new MultiCollectionEnumerator<T>(collections);
    }

    public IEnumerator<T> GetEnumerator()
    {
        enumerator.Reset();
        return enumerator;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        enumerator.Reset();
        return enumerator;
    }


    private class MultiCollectionEnumerator<T> : IEnumerator<T>
    {
        private IEnumerable<T>[] collections;
        private int currentIndex;
        private IEnumerator<T> currentEnumerator;

        public MultiCollectionEnumerator(IEnumerable<T>[] collections)
        {
            this.collections = collections;
            this.currentIndex = -1;
        }

        public T Current
        {
            get
            {
                if (currentEnumerator != null)
                    return currentEnumerator.Current;
                else
                    return default(T);
            }
        }

        public void Dispose()
        {
            if (currentEnumerator != null)
                currentEnumerator.Dispose();
        }

        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public bool MoveNext()
        {
            if (currentIndex >= collections.Length)
                return false;
            if (currentIndex < 0)
            {
                currentIndex = 0;
                if (collections.Length > 0)
                    currentEnumerator = collections[0].GetEnumerator();
                else
                    return false;
            }
            while (!currentEnumerator.MoveNext())
            {
                currentEnumerator.Dispose();
                currentEnumerator = null;

                currentIndex++;
                if (currentIndex >= collections.Length)
                    return false;
                currentEnumerator = collections[currentIndex].GetEnumerator();
            }
            return true;
        }

        public void Reset()
        {
            if (currentEnumerator != null)
            {
                currentEnumerator.Dispose();
                currentEnumerator = null;
            }
            this.currentIndex = -1;
        }
    }

}
like image 577
Bryce Wagner Avatar asked May 19 '10 00:05

Bryce Wagner


People also ask

What is IEnumerable collection in C#?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.

Is IEnumerable a collection?

IEnumerable<T> is an interface that represents a sequence. Now; collections can usually be used as sequences (so... List<T> implements IEnumerable<T> ), but the reverse is not necessarily true. In fact, it isn't strictly required that you can even iterate a sequence ( IEnumerable<T> ) more than once.

What is difference between IEnumerable and IEnumerator in C#?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. This works for readonly access to a collection that implements that IEnumerable can be used with a foreach statement. IEnumerator has two methods MoveNext and Reset. It also has a property called Current.


2 Answers

Try the SelectMany extension method added in 3.5.

IEnumerable<IEnumerable<int>> e = ...;
foreach ( int cur in e.SelectMany(x => x)) {
  Console.WriteLine(cur);
}

The code SelectMany(x => x) has the effect of flattening a collection of collections into a single collection. This is done in a lazy fashion and allows for straight forward processing as shown above.

If you only have C# 2.0 available, you can use an iterator to achieve the same results.

public static IEnumerable<T> Flatten<T>(IEnumerable<IEnumerable<T>> enumerable) {
  foreach ( var inner in enumerable ) {
    foreach ( var value in inner ) {
      yield return value;
    }
  }
}
like image 200
JaredPar Avatar answered Nov 14 '22 17:11

JaredPar


Just use the Enumerable.Concat() extension method to "concatenate" two IEnumerables. Don't worry, it doesn't actually copy them into a single array (as you might infer from the name), it simply allows you to enumerate over them all as if they were one IEnumerable.

If you have more than two then Enumerable.SelectMany() would be better.

like image 20
EMP Avatar answered Nov 14 '22 18:11

EMP