Safari Jones
Safari Jones

Reputation: 500

C# interweave two uneven List into a new List

I have two List, both of different lengths. What I am trying to achieve is a third List that contains the first element from list1, then the first element from list2, then second element from list1, and second element from list2, and so on until one of the two has been exhausted (they're uneven), and then just add in any remaining items from that list.

result should have the same number of items as list1 and list2 combined.

I cannot use something like Union.ToList() as that doesn't interweave the two, it just adds all of the items from for example list1 to the bottom of list2 and outputs it as result. I tried .Zip(Linq) however that seems to take in the two elements and merged them into a single element (namely concatenating the two strings into one longer string).

List<string> list1 = new List<string>(){
            "4041",
            "4040"              
        };

List<string> list2 = new List<string>(){ 
            "4039",
            "4044", 
            "4075", 
            "4010",
            "4100",
            "4070", 
            "4072" 
        };


// Ideal result:    
result = { "4041",
      "4039",
      "4040"  
      "4044",      
      "4075", 
      "4010",
      "4100",
      "4070", 
      "4072" 
}; 

Upvotes: 5

Views: 2287

Answers (5)

Leedan Johnson
Leedan Johnson

Reputation: 311

My take, allows for a variable number of IEnumerables to interleave

public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] enumerables)
        {
            var enumerators = enumerables.Select(e => e.GetEnumerator()).ToList();
            while (enumerators.Any())
            {
                enumerators.RemoveAll(e => {
                    var ended = !e.MoveNext();
                    if (ended) e.Dispose();
                    return ended;
                });

                foreach (var enumerator in enumerators)
                    yield return enumerator.Current;
            }
        }

Upvotes: 0

Firo
Firo

Reputation: 30813

longer but probably more efficient

List<string> result = new List<string>();
using (var enumerator1 = list1.GetEnumerator())
using (var enumerator2 = list2.GetEnumerator())
{
    int countBefore;
    do
    {
        countBefore = result.Count;
        if (enumerator1.MoveNext())
            result.Add(enumerator1.Current);
        if (enumerator2.MoveNext())
            result.Add(enumerator2.Current);
    } while (countBefore < result.Count);
}

Upvotes: 1

bingles
bingles

Reputation: 12213

If you don't need to keep the original lists intact, you can use a while loop to pop items off the front of each list:

while(list1.Count > 0 || list2.Count > 0)
{
    if(list1.Count > 0)
    {
        combinedList.Add(list1[0]);
        list1.RemoveAt(0);
    } 

    if(list2.Count > 0)
    {
        combinedList.Add(list2[0]);
        list2.RemoveAt(0);
    } 
}

Not quite as terse as Linq but easy to read and very clear what is going on.

Upvotes: 3

Barak Itkin
Barak Itkin

Reputation: 4877

int length = Math.Min(list1.Count, list2.Count);

// Combine the first 'length' elements from both lists into pairs
list1.Take(length)
.Zip(list2.Take(length), (a, b) => new int[] { a, b })
// Flatten out the pairs
.SelectMany(array => array)
// Concatenate the remaining elements in the lists)
.Concat(list1.Skip(length))
.Concat(list2.Skip(length));

Upvotes: 6

Selman Gen&#231;
Selman Gen&#231;

Reputation: 101701

I think you are looking for something like this:

list1
.SelectMany((x,idx) => new[] { x, list2[idx] })
.Concat(list2.Skip(list1.Count));

Fiddle

Upvotes: 0

Related Questions