2017-09-30 3 views
2

Ich suche nach einer Möglichkeit, eine beobachtbare Sequenz in separate Sequenzen aufzuspalten, die ich basierend auf einem gegebenen Prädikat unabhängig verarbeiten kann. So etwas wie dies wäre ideal:Partitionieren von Observablen in C#

var (evens, odds) = observable.Partition(x => x % 2 == 0); 
var strings = evens.Select(x => x.ToString()); 
var floats = odds.Select(x => x/2.0); 

Der nächstgelegene ich habe in der Lage zu kommen mit tut zwei where Filter, aber das erfordert die Bedingung Auswertung und Verarbeitung der Quellsequenz zweimal, was ich nicht bin wild Über.

observable = observable.Publish().RefCount(); 
var strings = observable.Where(x => x % 2 == 0).Select(x => x.ToString()); 
var floats = observable.Where(x => x % 2 != 0).Select(x => x/2.0); 

F # scheint dies mit Observable.partition<'T> und Observable.split<'T,'U1,'U2> gute Unterstützung zu haben, aber ich habe nicht in der Lage gewesen, etwas Äquivalent für C# zu finden.

+0

Sie können immer in der # Bibliothek F ziehen und es verwenden, von C# –

+0

Suchen Im Quellcode von F # sieht es so aus, als würde es nur zwei Filter auf den Quelldatenstrom anwenden, so dass es im Grunde genommen dasselbe ist wie mein Vorschlag mit zwei 'wheres'. – spencercw

Antwort

0

Wie wäre es so etwas wie

var (odds,evens) = (collection.Where(a=> a % 2 == 1), collection.Where(a=> a % 2 == 0));? 

oder wenn Sie basierend partitionieren möchten unter einer Bedingung

Func<int,bool> predicate = a => a%2==0; 

var (odds,evens) = (collection.Where(a=> !predicate(a)), collection.Where(a=> predicate(a))); 

ich es denke nicht arbeiten rund um die Tatsache, dass Sie die Elemente zweimal auf diese Weise durchlaufen, Was sonst noch getan werden könnte wäre, eine Methode zu haben, die ein Prädikat akzeptiert und die 2 getrennten Sammlungen weitergibt und sie in einer Iteration in einer foreach oder für.

Etwas wie folgt aus:

var collection = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9}; 

Func<int,bool> predicate = a => a%2==0; 
var odds = new List<int>(); 
var evens = new List<int>(); 

Action<List<int>, List<int>, Func<int, bool>> partition = (collection1, collection2, pred) => 
{ 
    foreach (int element in collection) 
    { 
     if (pred(element)) 
     { 
      collection1.Add(element); 
     } 
     else 
     { 
      collection2.Add(element); 
     } 
    } 
}; 

partition(evens, odds, predicate); 

Aufbauend auf der letzten Idee, suchen Sie so etwas wie?

public static (ObservableCollection<T>, ObservableCollection<T>) Partition<T>(this ObservableCollection<T> collection, Func<T, bool> predicate) 
{ 
    var collection1 = new ObservableCollection<T>(); 
    var collection2 = new ObservableCollection<T>(); 

    foreach (T element in collection) 
    { 
     if (predicate(element)) 
     { 
      collection1.Add(element); 
     } 
     else 
     { 
      collection2.Add(element); 
     } 
    } 

    return (collection1, collection2); 
} 
+0

Danke für Ihre Ideen. Ihr Vorschlag mit der "ObservableCollection" ist in der richtigen Richtung, aber ich habe es mit beobachtbaren Sequenzen ("IObservable") und nicht mit Sammlungen zu tun, daher muss er die neuen Sequenzen sofort zurückgeben, weil sie unendliche Sequenzen sein könnten. Ich habe etwas gekocht, was ein Paar "Subjekts" erzeugt, aber es ist irgendwie groß und ich habe auf etwas Vorheriges gehofft. – spencercw

+0

Hmm, jetzt kommt mir nichts in den Sinn, das in Echtzeit in einem Tupel "nachgeben" würde, vielleicht anstatt sie innerhalb der Methode zu erstellen, die IO-Server weiterzugeben und die Aufrufe abzufangen, wenn sich diese Sequenzen ändern. – Vnvizitiu

2

A GroupBy kann die „beobachten zweimal“ Einschränkung entfernen, wenn Sie noch mit Where Klauseln Ende folgendes haben:

public static class X 
{ 
    public static (IObservable<T> trues, IObservable<T> falsies) Partition<T>(this IObservable<T> source, Func<T, bool> partitioner) 
    { 
     var x = source.GroupBy(partitioner).Publish().RefCount(); 
     var trues = x.Where(g => g.Key == true).Merge(); 
     var falsies = x.Where(g => g.Key == false).Merge(); 
     return (trues, falsies); 
    } 
} 
+0

Schön. Ich dachte mir, ich könnte etwas mit 'GroupBy' machen, aber die' Merge' war der Schritt, den ich vermisste. Vielen Dank. – spencercw

+0

Oh, tatsächlich sieht es so aus, als würde die Quellsequenz zweimal ausgewertet, wenn Sie das tun. Verdammt – spencercw

+0

Hoppla, faul von mir. Aktualisierter Code: Var x sollte veröffentlicht werden + Refcounted. Der Vorteil bleibt, dass wenn Ihre Filterfunktion teuer ist, diese nur einmal ausgeführt wird. – Shlomo

Verwandte Themen