HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Is there an easier way to use Rx's GroupBy operator?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
useoperatorwaygroupbythereeasier

Problem

I'm just playing with the concept of GroupBy inside Rx. So, I wondered how hard it would be to write a console application that continuously reads lines, groups them by similar words and just prints out the word among the current count of how often the word was written before. It really was a breeze to do that but I wonder if my attempt could be rewritten in a more elegant way. Especially if I can get rid of the nested Subscribes.

Here is what I have:

static void Main(string[] args)
{
    var subject = new Subject();

    var my = subject.GroupBy(x => x);

    my.Subscribe(x => x.Scan(new { Chars = string.Empty, Count = 0},
                                 (a, chars) => new { Chars = chars, Count = a.Count + 1})
      .Subscribe(result => Console.WriteLine("You typed {0} {1} times", result.Chars, 
                 result.Count)));

    while (true)
    {
        subject.OnNext(Console.ReadLine());
    }
}


Result

test
you typed test 1 times
test
you typed test 2 times
hallo
you typed test 1 times
test
you typed test 3 times
...

Solution

You could use also use a counter provider per grouping observable, in the form of zipping a large stream of integers, like so:

static void Main(string[] args)
{
    var input = GetConsoleLines().ToObservable().TakeWhile(line => !string.IsNullOrEmpty(line));
    input.GroupBy(word => word)
         .SelectMany(grouping => grouping.Zip(Observable.Range(1, int.MaxValue), (s, i) => new Tuple(s, i)))
         .Subscribe(r => Console.WriteLine($"you typed {r.Item1} {r.Item2} times"));
}

public static IEnumerable GetConsoleLines()
{
    while(true)
        yield return Console.ReadLine();
}

Code Snippets

static void Main(string[] args)
{
    var input = GetConsoleLines().ToObservable().TakeWhile(line => !string.IsNullOrEmpty(line));
    input.GroupBy(word => word)
         .SelectMany(grouping => grouping.Zip(Observable.Range(1, int.MaxValue), (s, i) => new Tuple<string, int>(s, i)))
         .Subscribe(r => Console.WriteLine($"you typed {r.Item1} {r.Item2} times"));
}

public static IEnumerable<string> GetConsoleLines()
{
    while(true)
        yield return Console.ReadLine();
}

Context

StackExchange Code Review Q#5989, answer score: 2

Revisions (0)

No revisions yet.