Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Join two lists of different length

Tags:

c#

linq

I am using LINQ:

List<String> listA = new List<string>{"a", "b", "c", "d", "e", "f", "g"};  
List<String> listB = new List<string>{"1", "2", "3"};  

Desired result:

{"a", "1", "b", "2", "c", "3", "d", "1", "e", "2", "f", "3", "g", "1"}  

I tried but fail:

var mix = ListA.Zip(ListB, (l1, l2) => new[] { l1, l2 }).SelectMany(x => x);  
//Result : {"a", "1", "b", "2", "c", "3"}  

var mix = ListA.Zip(ListB, (a, b) => new[] { a, b })
        .SelectMany(x => x)
        .Concat(ListA.Count() < ListB.Count() ? ListB.Skip(ListA.Count()) : ListA.Skip(ListB.Count()))
        .ToList();  
//Result : {"a", "1", "b", "2", "c", "3", "d", "e", "f", "g"}  

How can I do this using LINQ?

like image 442
spiry Avatar asked Nov 06 '18 09:11

spiry


People also ask

How do you interleave two lists in Python?

chain() + zip() zip() can be used to link both the lists and then chain() can used to perform the alternate append of the elements as desired.

How do you interleave three lists in Python?

To interleave multiple lists of the same length in Python, we can use list comprehension and zip . We have 3 lists l1 , l2 , and l3 . And then we put them into the lists list. Then to interleave all the lists, we call zip with all the lists in lists as arguments.


Video Answer


4 Answers

This works, even if I am not sure why you need it as linq expression:

var mix = Enumerable
           .Range(0, Math.Max(listA.Count, listB.Count))
           .Select(i => new[] { listA[i % listA.Count], listB[i % listB.Count] })
           .SelectMany(x => x);
like image 97
gofal3 Avatar answered Oct 13 '22 07:10

gofal3


And how about implement your own wersion of Zip?

using System;
using System.Collections.Generic;
using System.Linq;

namespace SO
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            List<String> listA = new List<string> {"a", "b", "c", "d", "e", "f", "g"};
            List<String> listB = new List<string> {"1", "2", "3"};

            var mix = listA.ZipNew(listB, (l1, l2) => new[] {l1, l2}).SelectMany(x => x);

            foreach (var m in mix)
            {
                Console.WriteLine(m);
            }
        }
    }

    public static class Impl
    {
        public static int A(this int a)
        {
            return 1;
        }

        public static IEnumerable<TResult> ZipNew<TFirst, TSecond, TResult>( 
            this IEnumerable<TFirst> first, 
            IEnumerable<TSecond> second, 
            Func<TFirst, TSecond, TResult> resultSelector) 
        { 
            using (IEnumerator<TFirst> iterator1 = first.GetEnumerator()) 
            using (IEnumerator<TSecond> iterator2 = second.GetEnumerator())
            {
                var i1 = true;
                var i2 = true;
                var i1Shorter = false;
                var i2Shorter = false;
                var firstRun = true;


                while(true) 
                {
                    i1 = iterator1.MoveNext();
                    i2 = iterator2.MoveNext();

                    if (!i1 && (i1Shorter || firstRun))
                    {
                        iterator1.Reset();
                        i1 = iterator1.MoveNext();
                        i1Shorter = true;
                        firstRun = false;
                    }

                    if (!i2 && (i2Shorter || firstRun))
                    {
                        iterator2.Reset();
                        i2 = iterator2.MoveNext();
                        i2Shorter = true;
                        firstRun = false;
                    }

                    if (!(i1 && i2))
                    {
                        break;
                    }

                    yield return resultSelector(iterator1.Current, iterator2.Current); 
                }
            } 
        }
    }
}
like image 38
BWA Avatar answered Oct 13 '22 06:10

BWA


There is only version that tries to do the right thing and uses the right approach so far and it's the one by BWA. This can be simplified a little bit to this extension so I'll leave it here for reference:

public static IEnumerable<TResult> ZipLoop<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
{
    if (first is null) throw new ArgumentNullException(nameof(first));
    if (second is null) throw new ArgumentNullException(nameof(second));
    if (resultSelector is null) throw new ArgumentNullException(nameof(resultSelector));

    var (firstCycle, secondCycle) = (false, false);

    var e1 = first.GetEnumerator();
    var e2 = second.GetEnumerator();
    try
    {
        while (true)
        {
            if (!TryMoveNextOrLoop(first, ref e1, ref firstCycle)) yield break;
            if (!TryMoveNextOrLoop(second, ref e2, ref secondCycle)) yield break;
            if (firstCycle && secondCycle) yield break;
            yield return resultSelector(e1.Current, e2.Current);
        }
    }
    finally
    {
        e1?.Dispose();
        e2?.Dispose();
    }
}

private static bool TryMoveNextOrLoop<T>(IEnumerable<T> source, ref IEnumerator<T> enumerator, ref bool cycle)
{
    if (!enumerator.MoveNext())
    {
        cycle = true;
        enumerator.Dispose();
        enumerator = source.GetEnumerator();
        return enumerator.MoveNext();           
    }
    return true;
}
like image 38
t3chb0t Avatar answered Oct 13 '22 05:10

t3chb0t


I would go with the for loop approach

List<String> listA = new List<string> { "a", "b", "c", "d", "e", "f", "g" };
List<String> listB = new List<string> { "1", "2", "3" };

List<string> listC = new List<string>();
for (int i = 0; i < Math.Max(listA.Count, listB.Count); i++)
{
    listC.Add(listA[i % listA.Count]);
    listC.Add(listB[i % listB.Count]);
}
like image 1
fubo Avatar answered Oct 13 '22 05:10

fubo