Earl
Earl

Reputation: 293

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

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)

Upvotes: 15

Views: 17668

Answers (6)

Paul Ruane
Paul Ruane

Reputation: 38580

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

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

Which can be simplified as ValueTuple is implied by the parentheses on the right side of the =>. (On the left side they enclose the lambda parameters.):

var pairs = listA.Zip(listB, (a, b) => (a, b));

But can actually be simplified further as there is an overload that creates tuples for us:

var pairs = listA.Zip(listB);

Example using this resultant enumerable:

foreach (ValueTuple<A, B> pair in pairs)
{
    A a = pair.First;
    B b = pair.Second;
}

One could use var to hide the type here, but we can actually go a step further and pattern matching in the loop to bind these fields to variables:

foreach (var (a, b) in pairs)
{
    ...
}

Before .NET 4.7 you will need to use Tuple instead of ValueTuple, specify the type explicitly and use Item1 and Item2 instead of First and Second.

Upvotes: 46

bph
bph

Reputation: 11258

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)

Upvotes: 0

qrjo
qrjo

Reputation: 201

.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;
}

Upvotes: 3

Grynn
Grynn

Reputation: 1284

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

Upvotes: 4

Jonathan Dickinson
Jonathan Dickinson

Reputation: 9218

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.

Upvotes: 5

Tigran
Tigran

Reputation: 62248

I would suggest to use a List of tupples

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

Upvotes: 1

Related Questions