15

I have two lists of objects. List A and List B. I need to create List C which combines List A and List B into pairs. For example:

List A
object a1
object a2
object a3

List B
object b1
object b2
object b3

List C (creates pairs)
object c1 (object a1, object b1)
object c2 (object a2, object b2)
object c3 (object a3, object b3)
Earl
  • 283
  • 2
  • 7
  • 14

6 Answers6

46

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.

Jim G.
  • 15,141
  • 22
  • 103
  • 166
Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
5

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.

Community
  • 1
  • 1
Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60
  • 1
    I would use Paul's solution if I were able to use the 4.0 framework but this works on 3.5 so thanks! – Earl Aug 18 '11 at 16:44
  • 1
    @Earl How does this work on 3.5 ? `Tuple` is [only available since 4.0](http://msdn.microsoft.com/es-es/library/system.tuple%28v=vs.100%29.aspx). – J.A.I.L. Oct 21 '13 at 13:16
4

Something like this:

 var listA = new object[] { "1", "2", "3" };
 var listB = new object[] { "a", "b", "c" };
 var listC = Enumerable.Zip(listA,listB, (x,y)=>new {x,y});

 foreach (var item in listC)
 {
    Console.WriteLine("{0},{1}", item.x,item.y);
 }

Output: 1,a 2,b 3,c

thumbmunkeys
  • 20,606
  • 8
  • 62
  • 110
Grynn
  • 1,254
  • 11
  • 18
3

.NET Core 3 has a new overload for the Zip method. It takes two IEnumerables and creates one IEnumerable containing value tuples with the elements from both input IEnumerables.

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

You can use the result in multiple ways:

foreach((A a, B b) in pairs)
{
    // Do something with a and b
}
foreach(var (a, b) in pairs)
{
    // Do something with a and b
}
foreach(var pair in pairs)
{
    A a = pair.a;
    B b = pair.b;
}
qrjo
  • 101
  • 2
  • 6
1

I would suggest to use a List of tupples

http://msdn.microsoft.com/en-us/library/system.tuple.aspx.

Tigran
  • 61,654
  • 8
  • 86
  • 123
0

A potential solution using System.Linq Enumerable.Zip method (without specifying a function argument Func) would be as follows (note I've used ints to provide a simple concrete example):

List<int> list1 = new List<int>() {1,2,3,4,5};
List<int> list2 = new List<int>() {5,4,3,2,1};

IEnumerable<(int, int)> pairs;
pairs = list1.Zip(list2);
Console.WriteLine(string.Join(", ", pairs));

Outputs:

(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)
bph
  • 10,728
  • 15
  • 60
  • 135