我有一个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发出新值时运行,但我无法找到解决方案。
任何帮助都将不胜感激。
答案 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))
)
);
});