Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I merge (or zip) two IEnumerables together?

I have an IEnumerable<T> and an IEnumerable<U> that I want merged into an IEnumerable<KeyValuePair<T,U>> where the indexes of the elements joined together in the KeyValuePair are the same. Note I'm not using IList, so I don't have a count or an index for the items I'm merging. How best can I accomplish this? I would prefer a LINQ answer, but anything that gets the job done in an elegant fashion would work as well.

like image 924
Erik Forbes Avatar asked Mar 25 '09 18:03

Erik Forbes


3 Answers

Note: As of .NET 4.0, the framework includes a .Zip extension method on IEnumerable, documented here. The following is maintained for posterity and for use in .NET framework version earlier than 4.0.

I use these extension methods:

// From http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part-3-intermezzo-linq-s-new-zip-operator.aspx
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> func) {
    if (first == null) 
        throw new ArgumentNullException("first");
    if (second == null) 
        throw new ArgumentNullException("second");
    if (func == null)
        throw new ArgumentNullException("func");
    using (var ie1 = first.GetEnumerator())
    using (var ie2 = second.GetEnumerator())
        while (ie1.MoveNext() && ie2.MoveNext())
            yield return func(ie1.Current, ie2.Current);
}

public static IEnumerable<KeyValuePair<T, R>> Zip<T, R>(this IEnumerable<T> first, IEnumerable<R> second) {
    return first.Zip(second, (f, s) => new KeyValuePair<T, R>(f, s));
}

EDIT: after the comments I'm obliged to clarify and fix some things:

  • I originally took the first Zip implementation verbatim from Bart De Smet's blog
  • Added enumerator disposing (which was also noted on Bart's original post)
  • Added null parameter checking (also discussed in Bart's post)
like image 66
Mauricio Scheffer Avatar answered Sep 20 '22 22:09

Mauricio Scheffer


As a update to anyone stumbling across this question, .Net 4.0 supports this natively as ex from MS:

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };

var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);

Documentation:

The method merges each element of the first sequence with an element that has the same index in the second sequence. If the sequences do not have the same number of elements, the method merges sequences until it reaches the end of one of them. For example, if one sequence has three elements and the other one has four, the result sequence will have only three elements.

like image 35
Lasse Espeholt Avatar answered Sep 24 '22 22:09

Lasse Espeholt


Think about what you're asking a bit more closely here:

You want to combine two IEnumerables in which "the indexes of the elements joined together in the KeyValuePair are the same", but you "don't have a count or an index for the items I'm merging".

There's no guarantee your IEnumerables are even sorted or unsorted. There's no correlation between your two IEnumerable objects, so how can you expect to correlate them?

like image 35
Welbog Avatar answered Sep 23 '22 22:09

Welbog