正如我们所知,我们可以使用ObservableCollection来观察集合的变化。
那很好。
但是如何处理不可变列表的更改?
例如:我有IObservable<ImmutableArray<int>>
,这个蒸汽的序列可能是:
第一个:1、2、3、4、5
秒:1、2、3、4、5、6<----(绑定到视图时可能会出现一些性能问题。)
第三:3,4
有什么优雅的方法(或一些库)可以转换IObservable<ImmutableArray<int>> to ObservableCollection<int> ?
吗
然后我们可以观察ObservableCollection通知事件:
第一:添加事件1、2、3、4、5
第二:添加事件6,7<----(太酷了!)
第三:删除事件1、2、5、6
非常感谢。
这可能是一种有点天真的方法,但这是你想要的吗?
source
.Subscribe(ia =>
{
var ia2 = ia.ToArray();
var adds = ia2.Except(oc).ToArray();
var removes = oc.Except(ia2).ToArray();
foreach (var a in adds)
{
oc.Add(a);
}
foreach (var r in remove)
{
oc.Remove(r);
}
});
经过一些研究,我对自己的问题有了答案。
最好的解决方案应该是Levenstein距离。
计算过程大致如下:
-
确定插入-删除替换成本。(插入=1,删除=1,替换=2)
-
计算Levenstein距离,得到矩阵。
-
最短路径和对齐的回溯矩阵。(这很像A*寻路,在生成矩阵时设置回溯点,并在回溯后获得最短路径)
因此,这个问题可以结束。
我实际上写了一个nuget包,它可以自动为您完成
https://github.com/Weingartner/ReactiveCompositeCollections
部分代码使用不可变列表之间的差异来生成ObservableCollection更改事件。
进行区分的代码使用DiffLib
public static IObservable<List<DiffElement<T>>>
ChangesObservable<T>
( this ICompositeList<T> source
, IEqualityComparer<T>comparer = null
)
{
return source
.Items // IObservable<ImmutableList<T>>
.StartWith(ImmutableList<T>.Empty)
.Buffer(2, 1).Where(b => b.Count == 2)
.Select(b =>
{
var sections = Diff.CalculateSections(b[0], b[1], comparer);
var alignment = Diff.AlignElements
(b[0], b[1], sections, new BasicReplaceInsertDeleteDiffElementAligner<T>());
return alignment.ToList();
});
}
在另一种方法中,可以将其转换为ObservableCollection
internal ReadOnlyObservableCollection
( ICompositeList<T> list
, System.Collections.ObjectModel.ObservableCollection<T> collection
, IEqualityComparer<T> eq
) : base(collection)
{
_List = list;
_Collection = collection;
_Disposable = list.ChangesObservable(eq)
.Subscribe(change =>
{
int i = 0;
foreach (var diff in change)
{
switch (diff.Operation)
{
case DiffOperation.Match:
break;
case DiffOperation.Insert:
_Collection.Insert(i, diff.ElementFromCollection2.Value);
break;
case DiffOperation.Delete:
_Collection.RemoveAt(i);
i--;
break;
case DiffOperation.Replace:
_Collection[i] = diff.ElementFromCollection2.Value;
break;
case DiffOperation.Modify:
_Collection[i] = diff.ElementFromCollection2.Value;
break;
default:
throw new ArgumentOutOfRangeException();
}
i++;
}
});
}