Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enumerable.Zip to enforce same lengths

Tags:

c#

linq

I found myself frequently need to use Enumerable.Zip(), but having it to ensure the two IEnumerables to have the same length (or both to be infinite). E.g., if one enumerable reaches the end but the other doesn't, I want it to throw. According to the doc, Zip() will just stop enumerating as soon as one of them comes to the end.

I ended up always need something like below. What's the most "built-in"/elegant way to address this?

void Foo(IEnumerable<int> a, IEnumerable<int> b)
{
    // caching them. they are not huge or infinite in my scenario
    var a = a.ToList();
    var b = b.ToList();

    if (a.Count() != b.Count())
    {
        throw ...;
    }

    Enumerable.Zip(a, b, ...);
}
like image 554
KFL Avatar asked Mar 13 '15 19:03

KFL


1 Answers

I would probably just reimplement Zip the way you want to. It's really pretty simple - the following is trivially adapted from MoreLINQ. You'll want to give it a better name, mind you...

public static IEnumerable<TResult> ZipForceEqual<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
{
    if (first == null) throw new ArgumentNullException("first");
    if (second == null) throw new ArgumentNullException("second");
    if (resultSelector == null) throw new ArgumentNullException("resultSelector");

    return ZipForceEqualImpl(first, second, resultSelector);
}

static IEnumerable<TResult> ZipForceEqualImpl<TFirst, TSecond, TResult>(
    IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
{
    using (var e1 = first.GetEnumerator())
    using (var e2 = second.GetEnumerator())
    {
        while (e1.MoveNext())
        {
            if (e2.MoveNext())
            {
                yield return resultSelector(e1.Current, e2.Current);
            }
            else
            {
                throw new InvalidOperationException("Sequences differed in length");
            }
        }
        if (e2.MoveNext())
        {
            throw new InvalidOperationException("Sequences differed in length");
        }
    }
}
like image 73
Jon Skeet Avatar answered Sep 18 '22 00:09

Jon Skeet