Gibt es eine einfachere Möglichkeit, den GroupBy-Operator von Rx zu verwenden?

  • Ich spiele gerade mit dem Konzept von GroupBy in Rx. Ich wunderte mich also, wie schwer es sein würde, eine Konsolenanwendung zu schreiben, die Zeilen kontinuierlich liest, sie nach ähnlichen Wörtern gruppiert und das Wort einfach unter der aktuellen Anzahl der zuvor geschriebenen Wörter ausgibt. Das war wirklich ein Kinderspiel, aber ich frage mich, ob mein Versuch eleganter geschrieben werden könnte. Besonders, wenn ich die verschachtelten Abonnenten loswerden kann.

    Folgendes habe ich:

     static void Main(string[] args)
    {
        var subject = new Subject<string>();
    
        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());
        }
    }
     

    Ergebnis

     test
    you typed test 1 times
    test
    you typed test 2 times
    hallo
    you typed test 1 times
    test
    you typed test 3 times
    ...
     
    08 September 2012
    Adamjiafu
3 answers
  • Sie können auch einen Zähleranbieter für beobachtbare Gruppierungen verwenden, indem Sie einen großen Strom von Ganzzahlen wie folgt zippen:

     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();
    }
     
    07 August 2017
    Mihai Pantea
  • Ok, hier ist noch einer! Ich denke, das ist einfacher zu lesen, da wir nicht auf die Scan-Methode angewiesen sind, um den String durch die Berechnung zu tragen:

         static void Main(string[] args)
        {
            var subject = new Subject<string>();
    
            subject
              .GroupBy(x => x)
              .SelectMany(x => x.Scan(0, (count, _) => ++count).Zip(x, (count, chars) => new { Chars = chars, Count = count}))
              .Subscribe(result => Console.WriteLine("You typed {0} {1} times", result.Chars, result.Count));
    
            while (true)
            {
                subject.OnNext(Console.ReadLine());
            }
        }
     

    Kann es noch besser sein?

    12 November 2011
    Christoph
  • Das sieht für mich viel sauberer aus. Es sollte auch gezeigt werden, wie viel hilfreicher es ist, wenn Sie Ihren Variablen aussagekräftigere Namen geben.

     static void Main(string[] args)
    {
        var lineReader = new Subject<string>();
    
        lineReader.GroupBy(line => line)
            .Subscribe(lineGroup =>
            {
                lineGroup.Scan(0, (acc, _) => ++acc)
                    .Subscribe(count =>
                    {
                        var line = lineGroup.Key;
                        var timeSuffix = count == 1 ? "" : "s";
                        Console.WriteLine("You typed {0} {1} time{2}.", line, count, timeSuffix);
                    });
            });
    
        String readLine;
        while ((readLine = Console.ReadLine()) != null)
            lineReader.OnNext(readLine);
    }
     
    12 March 2012
    Jeff MercadoChris