RxJava按任意值去除

时间:2015-09-14 17:03:35

标签: reactive-programming rx-java

仍在确定正确使用不同的Rx *运营商并偶然发现以下问题:

我有以下类型的模型集合:

class Model {

    final long timestamp;
    final Object data;

    public Model(long timestamp, Object data) {
        this.timestamp = timestamp;
        this.data = data;
    }
}

此集合按升序排序(按时间戳排序)。

我的目标 - 按“序列”对它们进行分组。 “序列” - 是元素序列,其中每个元素都非常接近其邻居:

----A-B-C-----D-E-F---H-I--->

在这种情况下,我有3个“序列”。轴上的位置由模型的timestamp属性(不是发射时间)定义。形成序列的最大距离应该是可配置的。

或者让我们采取更实际的例子:

List<Model> models = new ArrayList<Model>(10) {{
    add(new Model(0, null));
    add(new Model(5, null));
    add(new Model(10, null));
    add(new Model(100, null));
    add(new Model(108, null));
    add(new Model(111, null));
    add(new Model(115, null));
    add(new Model(200, null));
    add(new Model(201, null));
    add(new Model(202, null));
}};

在这种情况下,最大距离为10ms,我会得到3个序列 - (0,5,10),(100,108,111,115),(200,201,202)

此逻辑与debounce运算符非常相似。但不是实时去抖,我需要通过一些自定义属性进行去抖动。

如果时间戳代表发射时间,我会这样做:

List<Model> models = new ArrayList<Model>(10) {{
    add(new Model(0, null));
    add(new Model(5, null));
    add(new Model(10, null));
    add(new Model(100, null));
    add(new Model(108, null));
    add(new Model(111, null));
    add(new Model(115, null));
    add(new Model(200, null));
    add(new Model(201, null));
    add(new Model(202, null));
}};

Observable<Model> modelsObservable = Observable.from(models).share();

modelsObservable.buffer(modelsObservable.debounce(10, TimeUnit.MILLISECONDS))
        .subscribe(group -> {
            //this is one of my groups
        });

不一定需要进行辩护 - 我也在关注groupBy运算符,但我无法找出正确的分组标准。

2 个答案:

答案 0 :(得分:2)

我不会调度调度程序,但会利用缓冲区/窗口(取决于您是否需要下游可观察项或集合)和扫描。

在Rx.Net中,您可以通过以下方式实现:

        var models = new[] { 0, 5, 10, 100, 108, 111, 115, 200, 201, 202 }
            .ToObservable();

        var enrichedModels = models.Scan(
            new { Current = -1, Prev = -1 },
            (acc, cur) => new { Current = cur, Prev = acc.Current })
            .Skip(1).Publish();

        enrichedModels.Buffer(() => enrichedModels.SkipWhile(em => em.Current < em.Prev + 10))
            .Select(seq => seq.Select(em => em.Prev))
            .Subscribe(seq =>
            {
                Console.WriteLine(String.Join(",", seq));
            });

        enrichedModels.Connect();

结果:

0,5,10
100,108,111,115
200,201
如果源可观察性很高,则可能会跳过

发布/连接。 rx-java拥有相同的运算符,但不是匿名类型,我猜它们可以由元组或具体类替换。

答案 1 :(得分:1)

有点不同寻常,但您可以在这里使用TestScheduler,按数据值安排值排放,然后使用此调度程序的去抖技巧并提前移动虚拟时间。

TestScheduler s = new TestScheduler();
Scheduler.Worker w = s.createWorker();

PublishSubject<Object> subject = PublishSubject.create();

for (Model m : model) {
    w.schedule(() -> subject.onNext(m.data), 
        m.timestamp, TimeUnit.MILLISECONDS);
}

subject.buffer(subject.debounce(10, TimeUnit.MILLISECONDS, s))
    .subscribe(list -> ...);

s.advanceTimeBy(Long.MAX_VALUE / 2, TimeUnit.MILLISECONDS);

w.unsubscribe();

(有人试图在RxJava中实现虚拟时间调度程序,但讨论被放弃了,建议的实现被拒绝了。)