Jeff
Jeff

Reputation: 2271

How to partition IEnumerable(Of T) based on pattern in order of T?

Consider the following IEnumerable(Of String):

    Moe1
    Larry1
    Curly1
    Shemp1
    Curly1

    Moe2
    Larry1
    Curly1
    Shemp1
    Curly1
    Curly2
    Shemp2
    Curly1
    Curly2

    Larry2
    Curly1

    Larry3
    Shemp1

They're visually split here to make the pattern easier to see. I want to partition an IEnumerable(Of String) using the .StartsWith() predicate, into an IEnumerable(Of IEnumerable(Of String).

The rules are:

I'm using the .StartsWith("Moe"), etc. to identify the type of stooge, but I'm having a hard time figuring out how to partition this set using LINQ operators so that if Moe is present, he represents the head of a partition, but since Larry must exist in each partition, he might represent the head of a partition if a Moe does not precede him.

How can I create the IEnumerable(Of IEnumerable(Of String) so that the result is partitioned the way I display the set with blank lines?

If there are no suitable LINQ operators for something like this (I'm programming in VB.NET if there are subtle differences in capabilities between VB.NET & C#), and I simply need to write a method to do this, I can do that, but I thought this might be a problem that comes up and is solved easily with LINQ operators.

Thanks in advance.

Upvotes: 0

Views: 306

Answers (1)

NOtherDev
NOtherDev

Reputation: 9672

LINQ doesn't seem to be a solution here as you have pretty complex split condition and the data structure you want to produce is not typical for LINQ queries. AFAIK, the only LINQ operator that is intended to know more than one element of the sequence at once is Aggregate, but it doesn't fit here as we're doing something inverse from aggregating.

So your problem seems to be solved easier using classical loops, more less like that (not tested thoroughly):

public IEnumerable<IEnumerable<string>> Partition(IEnumerable<string> input)
{
    var currPartition = new List<string>();
    string prev = null;

    foreach (var elem in input)
    {
        if (ShouldPartition(prev, elem))
        {
            yield return currPartition;
            currPartition = new List<string>();
        }

        currPartition.Add(elem);
        prev = elem;
    }

    yield return currPartition;
}

private bool ShouldPartition(string prev, string elem)
{
    if (prev == null)
        return false;
    if (elem.StartsWith("Moe"))
        return true;
    if (elem.StartsWith("Larry"))
        return !prev.StartsWith("Moe");
    return false;
}

Upvotes: 2

Related Questions