[英]How to generate IObservable index delta on the go?
我有两个数据源。
其中一个是缓存列表,另一个是通过IObservable<T>
推送的新数据。
我想使用Rx找出需要对缓存列表A进行哪些操作,以使其顺序和内容与新数据相同。
我要寻找的是需要一个函数IEnumerable<T> a
和IObservable<T> b
,并返回一个可观察的上推操作(插入和删除), a
将使其等同于b
,而无需等待b
完成。
注意:我知道我无法修改列表或可观察的列表。 我不想
我只想知道什么操作,只要知道这些操作,就会以什么顺序将与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
。
考虑以下两个序列:
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 :
A = [
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是一个冷的可观察物,需要一些时间才能完成,但很快就会产生第一批物品。 因此, 一旦有足够的数据可用,就需要推送可观察的结果。
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>
实现非常相似的功能。 但是,我不想继续使用此解决方案,因为我想知道是否有可能仅通过组合诸如Aggregate
, Zip
或CombineLatest
类的Rx函数来解决它。
你怎么看?
似乎可以工作...:
void Main()
{
var a = new int?[] {150, 100, 70, 30, 20 };
var b = new int?[] {300, 200, 100, 70, 60, 50, 20 };
var result = IndexDelta(a, b);
result.Dump();
}
// Define other methods and classes here
IObservable<Tuple<int, bool>> IndexDelta(IEnumerable<int?> a, IEnumerable<int?> b)
{
var observable = Observable.Create<Tuple<int, bool>>(o => {
var indexA = 0;
var indexB = 0;
var aEnumerator = a.GetEnumerator();
var bEnumerator = b.GetEnumerator();
var aHasNext = aEnumerator.MoveNext();
var bHasNext = bEnumerator.MoveNext();
while(true) {
if (aHasNext == false && bHasNext == false) {
"Completed".Dump();
o.OnCompleted(); // both sequences are over
break;
}
var headA = aEnumerator.Current;
var headB = bEnumerator.Current;
headA.Dump("A");
headB.Dump("B");
Action reportDeletion = () => {
o.OnNext(Tuple.Create(indexA, false));
aHasNext = aEnumerator.MoveNext(); // this one is fast
};
Action reportInsertion = () => {
o.OnNext(Tuple.Create(indexB, true));
bHasNext = bEnumerator.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.Value.CompareTo(headA.Value)) {
case 0:
aHasNext = aEnumerator.MoveNext();
bHasNext = bEnumerator.MoveNext();
indexA++;
indexB++;
break;
case 1: // Fetched item is newer than source item
reportInsertion();
indexB++;
break;
case -1: // Source item is newer than fetched item
reportDeletion();
indexA++;
break;
}
}
return Disposable.Empty;
});
return observable;
}
该代码基于Richard的答案,但可与任何T
。
不过,我一直无法摆脱ToEnumerable
的诅咒-感谢您的帮助。
IObservable<Tuple<int, T, bool>> IndexDelta<T>(
IObservable<T> first, IObservable<T> second
)
where T : IComparable, IEquatable<T>
{
return Observable.Create<Tuple<int, T, bool>> (o => {
var a = first.ToEnumerable ().GetEnumerator ();
var b = second.ToEnumerable ().GetEnumerator ();
var indexA = -1;
var indexB = -1;
var hasNextA = true;
var hasNextB = true;
var headA = default(T);
var headB = default(T);
Action<bool> advanceA = (bool reportDeletion) => {
if (reportDeletion) {
o.OnNext (Tuple.Create (indexA, headA, false));
}
if (hasNextA = a.MoveNext ()) {
indexA++;
headA = a.Current;
}
};
Action<bool> advanceB = (bool reportInsertion) => {
if (reportInsertion) {
o.OnNext (Tuple.Create (indexB, headB, true));
}
if (hasNextB = b.MoveNext ()) {
indexB++;
headB = b.Current;
}
};
advanceA (false);
advanceB (false);
while (true) {
if (!hasNextA && !hasNextB) {
o.OnCompleted ();
break;
}
if (!hasNextA) {
advanceB (true);
continue;
}
if (!hasNextB) {
advanceA (true);
continue;
}
switch (headA.CompareTo (headB)) {
case 0:
advanceA (false);
advanceB (false);
break;
case 1:
advanceA (true);
break;
case -1:
advanceB (true);
break;
}
}
return Disposable.Create (() => {
a.Dispose ();
b.Dispose ();
});
});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.