How do I create a single list of object pairs from two lists in C#?

You could use the Enumerable.Zip() method in System.Linq.

IEnumerable<Tuple<A, B>> pairs = listA.Zip(listB, (a, b) => Tuple.Create(a, b));

Example using this resultant enumerable:

foreach (Tuple<A, B> pair in pairs)
{
    A a = pair.Item1;
    B b = pair.Item2;
}

Shame there's not an overload that automates the tupleation in .NET. Such an extension would look like this:

public static IEnumerable<Tuple<TFirst, TSecond>> Zip<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
    return first.Zip(second, Tuple.Create);
}

Which would mean you could then write code that looks like:

IEnumerable<Tuple<A, B>> pairs = listA.Zip(listB);

Note: Another option would be to create anonymous types instead of Tuple but the downside to this approach is that you would not be able to (usefully) pass the resultant IEnumerable out of the method it is created in owing to the type not having a name.


This would do it:

public static IEnumerable<Tuple<T, U>> CombineWith<T, U>(this IEnumerable<T> first, IEnumerable<U> second)
{
    using (var firstEnumerator = first.GetEnumerator())
    using (var secondEnumerator = second.GetEnumerator())
    {
        bool hasFirst = true;
        bool hasSecond = true;

        while (
            // Only call MoveNext if it didn't fail last time.
            (hasFirst && (hasFirst = firstEnumerator.MoveNext()))
            | // WARNING: Do NOT change to ||.
            (hasSecond && (hasSecond = secondEnumerator.MoveNext()))
            )
        {
            yield return Tuple.Create(
                    hasFirst ? firstEnumerator.Current : default(T),
                    hasSecond ? secondEnumerator.Current : default(U)
                );
        }
    }
}

Edit: I vastly prefer Paul's answer.

Tags:

C#

List

Object