tags:

views:

233

answers:

1

Having two lists of same object type. I want to join them using an interleave pattern where i items of the first list are separated by j items from the second list.

In essence:

First list

{a, b, c, d, e, f, g, h}

Second list

{0, 1, 2, 3, 4}

where the grouping count for the first list is 3 and for the second list is 2.

resulting in

{a, b, c, 0, 1, e, f, g, 2, 3, h, 4}

Is this possible with Linq?

+10  A: 

There's nothing within LINQ itself to do this - it seems a pretty specialized requirement - but it's fairly easy to implement:

public static IEnumerable<T> InterleaveWith<T>
   (this IEnumerable<T> first, IEnumerable<T> second,
    int firstGrouping, int secondGrouping)
{
    using (IEnumerator<T> firstIterator = first.GetEnumerator())
    using (IEnumerator<T> secondIterator = second.GetEnumerator())
    {
        bool exhaustedFirst = false;
        // Keep going while we've got elements in the first sequence.
        while (!exhaustedFirst)
        {                
            for (int i = 0; i < firstGrouping; i++)
            {
                 if (!firstIterator.MoveNext())
                 {
                     exhaustedFirst = true;
                     break;
                 }
                 yield return firstIterator.Current;
            }
            // This may not yield any results - the first sequence
            // could go on for much longer than the second. It does no
            // harm though; we can keep calling MoveNext() as often
            // as we want.
            for (int i = 0; i < secondGrouping; i++)
            {
                 // This is a bit ugly, but it works...
                 if (!secondIterator.MoveNext())
                 {
                     break;
                 }
                 yield return secondIterator.Current;
            }
        }
        // We may have elements in the second sequence left over.
        // Yield them all now.
        while (secondIterator.MoveNext())
        {
            yield return secondIterator.Current;
        }
    }
}
Jon Skeet
That was fast! Thank you very much. I'm trying it now...
Stecy
@Stecy: There was a bug in the first version. Try this one.
Jon Skeet
You still need to work on that; firstGrouping and secondGrouping are integers, not iterators as you use them in the for loops. But it's the best approach to the problem, hands down.
Randolpho
@Randolpho: Fixed (it was just the once.)
Jon Skeet
Just checked, and it compiles now. I really should try to get into the habit of compiling *before* posting...
Jon Skeet
Compiles fine but does not give the expected result. Seems to skip one element from the first list upon returning to the top of the while loop...
Stecy
Added an answer with corrected bug.
Stecy
@Stecy: I'll edit my answer to match, to avoid the bug propagating.
Jon Skeet
@Jon - you are right - my comment was incorrect. The danger of "one quick glance before I leg it" style comments. I must avoid that!
Marc Gravell
@Jon Skeet: LINQPad is your friend for compile-before-post coding. Much faster than firing up VS and loading a scratch project.
Randolpho
@Randolpho: Oh I certainly wouldn't fire up VS for anything so trivial - but I do have a lightweight text editor I tend to use. Must try LINQPad for this though. I've been considering an "SO-specific" editor with built-in useful things like finding MSDN links.
Jon Skeet
@Jon: Removed my comment about the correction since you've updated your answer.
Stecy