我有两个数据源。
其中一个是缓存列表,另一个是通过IObservable<T>
.
我想使用 Rx 找出需要对缓存列表 A 执行哪些操作,以使其顺序和内容与新数据相同。
我正在寻找一个函数,它接受一个IEnumerable<T> a
andIObservable<T> b
并返回一个 observable 来推动操作(插入和删除),a
这将使它与b
不等待b
完成相同。
注意:我知道我不能修改列表或 observable。我不想。
我只想知道,一旦知道这些操作,哪些操作,以什么顺序将其顺序和顺序与 A 相同的假设列表变成其顺序和顺序与 B 相同的列表。
a
和都是b
唯一的和排序的,T
实现IComparable<T>
和IEquatable<T>
。
public static IObservable<Tuple<int, bool>> IndexDelta<T>(
IEnumerable<T> a,
IObservable<T> b
) where T : IEquatable<T>, IComparable<T> {
// ???
}
我将int
在我的示例中使用 s。
什么?!
考虑这两个序列:
A: [150, 100, 70, 30, 20]
B: [300, 200, 100, 70, 60, 50, 20]
目标是找到一系列将 A 转换为 B 的删除/插入操作。认为A 是缓存数据源,B 是新数据,我想知道如何将这些更新转换为网格而不重新加载它。
行在两个源中排序。
我希望输出形式为
[(0, true), (1, true), (0, false), (3, false), (4, true), (5, true)]
稍后我将通过布尔标志对这些操作进行分组:
deleted: [0, 3]
inserted: [0, 1, 4, 5]
这将翻译成人类语言
删除 A 0和 A 3:
一个 = [
150, 100, 70,30, 20] = [100, 70, 20]将B 0、 B 1、 B 4、 B 5插入 A:
A = [ 300 , 200 , 100, 70, 60 , 50 , 20]
现在 A 与 B 相同。
要求
我要注意几件重要的事情:
A 是保证不会更改的列表。B 是一个冷的 observable,它需要一些时间才能完成,但很快就会产生第一个项目。因此,只要有足够的数据可用,就需要推送结果 observable。
保证项目在两个来源中都是唯一的。
IEquatable<T>
项目是不可变的,并且保证
IComparable<T>
在两个源中使用降序排序。最好针对添加到 B 左侧的新项目进行优化。这是最常见的情况。然而,考虑到它们的时间戳是适当的(不会破坏排序),项目可能会被删除或插入到任何其他位置。想想 iPhone 相机胶卷。
(*) 如果可能的话,我对纯功能解决方案感兴趣。
伪代码草图
我草拟了一个伪代码算法,它以命令式的方式实现了这一点。
我编造了Current
,和语义MoveNext
,但这个想法应该是有道理的。await
yield push
IObservable<Tuple<int, bool>> IndexDelta(a, b)
{
var indexA = 0;
var indexB = 0;
while (true) {
var headA = a.Current;
var headB = b.Current;
if (headA == null && headB == null) {
return yield break; // both sequences are over
}
var reportDeletion = () => {
yield push Tuple.Create(indexA, false);
await a.MoveNext(); // this one is fast
}
var reportInsertion = () => {
yield push Tuple.Create(indexB, true);
await b.MoveNext(); // can take a long time
}
if (headA == null) { // No source item at this position
reportInsertion();
continue;
}
if (headB == null) { // No fetched item at this position
reportDeletion();
continue;
}
switch (headB.CompareTo(headA)) {
case 0:
yield continue;
break;
case 1: // Fetched item is newer than source item
reportInsertion();
break;
case -1: // Source item is newer than fetched item
reportDeletion();
break;
}
indexA++;
indexB++;
}
}
我相信你可以用Subject<T>
. 但是我不想继续这个解决方案,因为我想知道是否可以纯粹通过组合 Rx 函数来解决它,例如Aggregate
,Zip
或CombineLatest
.
你觉得呢?你有没有什么想法?