2017-10-18 11 views
0

私はIObservable<ImmutableArray<T>>を持っています。それぞれのTに対応する削除コマンドはIObservable<Unit>です。私はユーザーがリスト内の項目の削除をクリックしたときに応答しようとしています。アイテムがリストに追加されると、Deleteコマンドのリスン(購読)を開始したいと考えています。アイテムがリストから削除されると、私は聴き取りを停止したい(購読中止)。アイテムXがリストに追加され、リストが何度も変更された場合、リストが変更されるたびに、Xを削除してから再登録するのではなく、Xの削除コマンドを1回だけ購読したことを確認したい。リスト内のアイテム内で観測可能なイベントをリッスンする

私は元々Switchコマンドを使ってこれを試みました。しかし、リストが変わるたびに、各アイテムの購読を中止して再購読している可能性があることに気付きました。これにより、PairwiseまたはScanを使用するのが難しくなります。リストが変更されるたびに、サブスクリプションが消去され、新しいものから開始されるからです。私は他の問題に遭遇したと思うが、それは一般的に、それらの購読者と購読者を除いて正解とは思えない。

私は答えがTakeUntilを使用する必要があると思います。私はリストを監視し、ペアワイズを行い、何が新旧削除されているかを常に知ることができます。その後、各アイテムを購読すると、そのアイテムが削除済みセットにあるTakeUntilを実行します。まあそれはアイデアですが、コードに問題があります。

これは、私が働いているものです:

interface IListItem { 
    IObservable<Unit> Delete { get; } 
    string ListItemName { get; } 
} 

IObservable<ImmutableList<IListItem>> _list; 

_list...something here...Subscribe(i=>{ 
    Console.WriteLine($"You requested to delete {i}!"); 
}); 
+0

'IObservable 'だけでなく 'IObservable >'に 'ImmutableList'がある理由を説明できますか? – Enigmativity

+0

現在のリスト全体を、observableの値ごとにプッシュしていれば、そのアイテムが以前にプッシュされたものから欠けているかどうかを確認するだけです - 知っておくと 'IObservable 'は必要ありませんアイテムが削除されたとき。あなたは説明を助けることができますか? – Enigmativity

+0

リストの各項目は、Edit、MoveUp、MoveDown、Deleteのようなコマンドを持つビューモデルです。削除すると、あなたを混乱させることになります。ユーザーがリスト内の特定の項目でMoveDownをクリックしたときを知りたい。ユーザーがアイテムを追加したり削除したりすると、リストが頻繁に変更され、現在リストにあるアイテムに関連するイベントだけをリスンしたいと考えています。 – JustinM

答えて

1

まあ、私はここにまともな答えを得たと思います。私が望むより少し長いが、私はそれがうまくいくと思う。誰かが短くて簡単なことを提案したいと思ったら、それを聞いてみたい。部分.Select(add => add.Delete.Select(_ => add).TakeUntil...は奇妙です。基本的には、特定のリスト項目に対してdeleteコマンドが呼び出されたときに、Deleteコマンドが呼び出されたリスト項目を返したいとします。

_items 
.Pairwise((before, after) => new 
{ 
    AddedToList = after.Except(before), 
    RemovedFromList = before.Except(after) 
}) 
.Publish(p => 
{ 
    var additions = p.SelectMany(i => i.AddedToList); 
    var removals = p.SelectMany(i => i.RemovedFromList); 
    return 
     additions 
     .Select(add => 
      add 
      .Delete 
      .Select(_ => add) 
      .TakeUntil(removals.Where(rem => rem == add))) 
     .Merge(); 
}).Subscribe(i => 
{ 
    // process the delete request on i 
    // at the end, submit the modified array to _items 
}); 
0

ここで私は建物を完成させました。それは私にとってかなりうまくいく。最初の部分は、2つのセットの違いを計算し報告するための汎用クラスです。

public class SetComparison<T> 
{ 
    private Lazy<IImmutableSet<T>> _added; 
    private Lazy<IImmutableSet<T>> _removed; 
    private Lazy<IImmutableSet<T>> _intersection; 

    public SetComparison(IEnumerable<T> previous, IEnumerable<T> current) 
    { 
     if (previous == null) throw new ArgumentNullException(nameof(previous)); 
     if (current == null) throw new ArgumentNullException(nameof(current)); 
     Previous = previous.ToImmutableHashSet(); 
     Current = current.ToImmutableHashSet(); 
     _added = new Lazy<IImmutableSet<T>>(() => Current.Except(Previous)); 
     _removed = new Lazy<IImmutableSet<T>>(() => Previous.Except(Current)); 
     _intersection = new Lazy<IImmutableSet<T>>(() => Current.Intersect(Previous)); 
    } 

    public IImmutableSet<T> Previous { get; } 
    public IImmutableSet<T> Current { get; } 
    public IImmutableSet<T> Added => _added.Value; 
    public IImmutableSet<T> Removed => _removed.Value; 
    public IImmutableSet<T> Intersection => _intersection.Value; 
} 

そして、これは順序を取り、アイテムのペアを一連の重複にそれを回すためにF#ペアごとのような汎用的な演算子です。

public static IObservable<TResult> Pairwise<TSource, TResult>(
    this IObservable<TSource> source, 
    Func<TSource, TSource, TResult> resultSelector) => 
    source.Scan(
     (default(TSource), default(TSource)), 
     (pair, current) => (pair.Item2, current)) 
     .Skip(1) 
     .Select(p => resultSelector(p.Item1, p.Item2)); 

これで2組のペアが作成されます。私は現在のセットにのみある観測に加入したいとき

public static IObservable<TResult> Merge<TResult, T>(this IObservable<SetComparison<T>> source, Func<T, IObservable<TResult>> result) => 
    source 
    .Publish(s => 
    { 
     var additions = s.SelectMany(i => i.Added); 
     var removals = s.SelectMany(i => i.Removed); 
     return 
      additions 
      .Select(add => result(add).TakeUntil(removals.Where(rem => rem.Equals(add)))) 
      .Merge(); 
    }); 

にだから今、私はこのような何かを

public static IObservable<SetComparison<T>> PairwiseSetComparison<T, TCollection>(this IObservable<TCollection> source) where TCollection : IEnumerable<T> => 
    source 
    .Pairwise((a, b) => new SetComparison<T>(a, b)); 


public static IObservable<SetComparison<T>> PairwiseSetComparison<T>(this IObservable<ImmutableArray<T>> source) => 
    source.Pairwise((a, b) => new SetComparison<T>(a, b)); 

そして最後に、この、...

_items 
.PairwiseSetComparison() 
.Merge(i => i.Delete.Select(_ => i)) 
.Subscribe(i=> /* user clicked Delete on item i */) 

これを行うもう1つの方法は、リスト内の各項目のDelete ICommandに、ユーザーが呼び出すときに実際に委任を実行することです。そうすれば、コマンドが呼び出されたときに実際に購読する必要はありません。私は自分のリストにあるアイテムを周囲にはまったく気づかないでおくのが好きです。

関連する問題