Jun 08, 2013

C# LINQ: Combine Multiple Sequences In Parallel

In this article, we will see how to join multiple arrays, lists or collections by order with LINQ. In other words, we can say Zip operation on multiple sequences. Assuming we have following arrays:


            int[] numbers = { 1, 2, 3 };
            string[] abc = { "a", "b", "c" };
            string[] pqr = { "p", "q", "r" };
            string[] xyz = { "x", "y", "z" };
            string[] words = { "one", "two", "three" };

Output:

Our objective is to combine all arrays and generate following output:

linq output combine

1. Using Join By Index:

In this way, we perform Linq join based on index:


 var output1 = (from n1 in numbers.Select((item, index) => new { item, index })
                          join n2 in abc.Select((item, index) => new { item, index }) on n1.index equals n2.index
                          join n3 in pqr.Select((item, index) => new { item, index }) on n2.index equals n3.index
                          join n4 in xyz.Select((item, index) => new { item, index }) on n3.index equals n4.index
                          join n5 in words.Select((item, index) => new { item, index }) on n4.index equals n5.index
                          select new { Number = n1.item, ABC = n2.item, PQR = n3.item, XYZ = n4.item, Words = n5.item }).ToList();

2. Using Zip Operator:

Zip operator processes two sequences, pairing items and applying a function to generate the values for the output sequence. For five arrays, this operator is used four times.


  var output2 = (numbers.Zip(abc, (first, second) => new { Number = first, ABC = second })
                .Zip(pqr, (first, second) => new { Number = first.Number, ABC = first.ABC, PQR = second })
                .Zip(xyz, (first, second) => new { Number = first.Number, ABC = first.ABC, PQR = first.PQR, XYZ = second })
                .Zip(words, (first, second) => new { Number = first.Number, ABC = first.ABC, PQR = first.PQR, XYZ = first.XYZ, Words = second })).ToList();

3. Using Extension Method:

If you are going to do this repeatedly, create an extension method for this:


 public static IEnumerable<TResult> Zip5<TFirst, TSecond, TThird, TFourth, TFifth, TResult>(
                      this IEnumerable<TFirst> first,
                      IEnumerable<TSecond> second,
                      IEnumerable<TThird> third,
                      IEnumerable<TFourth> fourth,
                      IEnumerable<TFifth> fifth,
                Func<TFirst, TSecond, TThird, TFourth, TFifth, TResult> resultSelector)
        {
            using (IEnumerator<TFirst> iterator1 = first.GetEnumerator())
            using (IEnumerator<TSecond> iterator2 = second.GetEnumerator())
            using (IEnumerator<TThird> iterator3 = third.GetEnumerator())
            using (IEnumerator<TFourth> iterator4 = fourth.GetEnumerator())
            using (IEnumerator<TFifth> iterator5 = fifth.GetEnumerator())
            {
                while (iterator1.MoveNext() && iterator2.MoveNext() && iterator3.MoveNext() && iterator4.MoveNext() && iterator5.MoveNext())
                {
                    yield return resultSelector(iterator1.Current, iterator2.Current, iterator3.Current, iterator4.Current, iterator5.Current);
                }
            }
        }

and use it as below:


 var output3 = numbers.Zip5(abc,pqr,xyz,words,
                (n1,n2,n3,n4,n5)=> new { Number = n1, ABC = n2, PQR = n3, XYZ = n4, Words = n5 }).ToList();

Hope, It helps. Let me know how you are doing it. Don't forget to share this post, if you like it.