Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select 2 items at a time?

Tags:

c#

regex

linq

I was writing a PascalCaseParser using Regex.Split and I came to the desire to select two items from an collection at a time.

This example code demonstrates.

void Main()
{
    string pascalCasedString = "JustLikeYouAndMe";
    var words = WordsFromPascalCasedString(pascalCasedString);
    words.Dump();
}

IEnumerable<string> WordsFromPascalCasedString(string pascalCasedString)
{
    var rx = new Regex("([A-Z])");
    return rx.Split(pascalCasedString)
             .Where(c => !string.IsNullOrEmpty(c))
             // how to select 2 elements at a time?
             ;
}

The result of above code is:

IEnumerable<String> (10 items)
J 
ust 
L 
ike 
Y 
ou 
A 
nd 
M 
e 

Every two elements of the collection make one result that I want the function WordsFromPascalCasedString to yield.

My questions is: How would you, in general, deal with a requirement to return two items at a time. I'm curious if there are any interesting non-brute-force approaches.

like image 510
Aaron Anodide Avatar asked Dec 09 '22 14:12

Aaron Anodide


1 Answers

Personally, I'd go with Simon Belanger's answer in this particular case. But in general, to select consecutive pairs, from an IEnumerable, you'd use this:

IEnumerable<Tuple<string, string>> WordsFromPascalCasedString(string pascalCasedString)
{
    var rx = new Regex("([A-Z])");
    var array = rx.Split(pascalCasedString)
                  .Where(c => !string.IsNullOrEmpty(c))
                  .ToArray();
    var items = Enumerable.Range(0, array.Length / 2)
                          .Select(i => Tuple.Create(array[i * 2], array[i * 2 + 1]);
}

Or this, which takes more effort, but it's reusable and more efficient:

IEnumerable<Tuple<T, T>> Pairs<T>(IEnumerable<T> input)
{
    var array = new T[2];
    int i = 0;
    foreach(var x in input)
    {
        array[i] = x;
        i = (i + 1) % 2;
        if (i == 0)
        {
            yield return Tuple.Create(array[0], array[1]);
        }
    }
}


IEnumerable<Tuple<string, string>> WordsFromPascalCasedString(string pascalCasedString)
{
    var rx = new Regex("([A-Z])");
    var output = rx.Split(pascalCasedString)
                   .Where(c => !string.IsNullOrEmpty(c));
    var items = Pairs(output);
}

It can easily be extended to groups of n:

IEnumerable<IEnumerable<T>> Batches<T>(IEnumerable<T> input, int n)
{
    var array = new T[n];
    int i = 0;
    foreach(var x in input)
    {
        array[i] = x;
        i = (i + 1) % n;
        if (i == 0)
        {
            yield return array.ToArray();
        }
    }

    if (i != 0)
    {
        yield return array.Take(i);
    }
}

A similar method exists in MoreLINQ.

like image 114
p.s.w.g Avatar answered Dec 11 '22 07:12

p.s.w.g