如何观察不可变列表 INotifyCollectionChanged?
How to observe Immutable List NotifyCollectionChanged?
正如我们所知,我们可以使用 ObservableCollection 观察集合的变化。
没关系。
但是如何处理 ImmutableList 改变了?
对于 example:I 有 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);
}
});
经过一些研究,我对自己的问题有了答案。
最好的解决方案应该是Levenshtein distance。
计算过程大致如下:
确定插入删除替代成本。 (插入=1,删除=1,替换=2)
计算编辑距离得到矩阵
最短路径和对齐的回溯矩阵。 (很像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++;
}
});
}
正如我们所知,我们可以使用 ObservableCollection 观察集合的变化。
没关系。
但是如何处理 ImmutableList 改变了?
对于 example:I 有 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);
}
});
经过一些研究,我对自己的问题有了答案。
最好的解决方案应该是Levenshtein distance。
计算过程大致如下:
确定插入删除替代成本。 (插入=1,删除=1,替换=2)
计算编辑距离得到矩阵
最短路径和对齐的回溯矩阵。 (很像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++;
}
});
}