如何使用rxjs根据时间过滤数组中的项?

时间:2017-06-28 21:04:02

标签: javascript arrays datetime rxjs

我有一个Observable,它发出的值是对象数组。发出的值如下所示:

[
    {
        id: 1,
        title: 'Foo',
        datetime: 'Thu Jun 29 2017 15:14:05 GMT-0400 (Eastern Daylight Time)'
    },
    {
        id: 2,
        title: 'Bar',
        datetime: 'Wed Jun 28 2017 12:10:42 GMT-0400 (Eastern Daylight Time)'
    }
]

请注意,datetime值为Date()个对象。

我想要做的是创建一个“即将发生的事件”列表,显示当前时间之后的所有事件。为此,我做了以下几点:

events$.subscribe(data => {
    const upcomingEvents = data.filter(item => item.datetime > new Date());
})

同样可以通过以下方式实现:

events$.map(data => {
    return data.filter(item => item.datetime > new Date());
})
.subscribe(data => {
    const upcomingEvents = data;
});

这种方法的问题在于,虽然它确实添加了任何通过过滤器的新项目,但是当当前时间超过项目时间时,它不会删除项目。

我知道这种行为是由于过滤器仅在Observable发出新值时运行,但我无法找到解决方案。

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:1)

这不是一个非常优雅的解决方案,但我能想到解决这个问题的唯一方法是合并两个流:第一个是你的observable,然后以某个任意间隔创建第二个时间戳流到“垃圾收集” “你的第一根弦。您需要向该合并流添加scan,以便在流的末尾维护具有过滤日期状态的累加器。所以这样的事情(警告,未经测试!):

// modeling your observable, not sure how you're creating it
const events$ = Rx.Observable.interval(3000)
  .concatMap(function (x) {
    return Rx.Observable
      .of([{title: "bar", datetime: new Date()}, {title: "foo", datetime: new Date()}])
  });

// Infinite timestamp stream
// this one checks every second, you may want to 
// check every minute, or every hour
const timestamps$ = Rx.Observable.interval(1000)
  .timestamp()
  .delay(0)
  .repeat(-1);

const merged$ = events$.merge(timestamps$);
const filtered$ = merged$
  // Starts with empty array until your first upcoming events array
  .startWith([])
  .scan((acc, val) => {
    if (val.timestamp) {
      acc = acc.filter(item => item.datetime.getTime() < val.timestamp);
      return acc
    } else {
      acc = val
      return acc
    }
  });

有点笨拙,但我现在无法想到一个更好的解决方案,将所有状态保持在rxjs内。

答案 1 :(得分:1)

你应该能够通过编写像这样的可观察对象来做你想做的事情:

const upcomingEvents$ = events$

  // Use switchMap so that each time events$ emits an array,
  // the observable that includes the upcoming events and the
  // to-be-merged updates of upcoming events is recomposed.

  .switchMap((events) => {

    function predicate(event) {
      return event.datetime.getTime() > Date.now();
    }

    // Filter the upcoming events and create a merged observable
    // that includes an observable of the initial upcoming events
    // and timer observables that will reapply the filter at the
    // appropriate times.

    const upcoming = events.filter(predicate);
    return Observable.merge(
      Observable.of(upcoming),
      ...upcoming.map(event => Observable
        .timer(event.datetime)
        .map(() => upcoming.filter(predicate))
      )
    );
  });