对于专业人士来说,问题可能很简单。尝试了多种方法后,我无法绕过它。
我有一个包含密钥列表的可观察observable1
。像
[
key1,
key3,
key4,
..
]
第二个可观察的observable2
包含数据,observable1
的某些键映射到。像
[
{key1, val1},
{key2, val2},
{key3, val3},
{key4, val4},
..
]
为简单起见,我们假设observable1
中的每个键都在observable2
中有相应的映射。
有了这种初始情况,我希望有一个可观察的observable3
仅发出来自observable2
的数据,该数据由observable1
中的密钥映射。
在给定的示例中,输出应该省略key2的数据,看起来像
[
{key1, val1},
{key3, val3},
{key4, val4},
..
]
在订阅和登录后,。
如何在rxjs中实现这一目标?顺便说一句,我使用的是打字稿。
答案 0 :(得分:0)
正如我在my comment中所概述的那样,你所描述的是不可能的。 Observable是(可能)无限的实时事件流。可观察的事件可以来自例如用户按下按钮或者在websocket上的消息。当事件到达observable2
时,您未必收到observable1
中的所有事件,因此您不知道其密钥是否在流中。
的可能性是询问某个对象的密钥是否存在于某个给定时间点observable1
上以前已收到的密钥集中。实现这一目标的良好,纯粹功能的方法是将observable1
聚合到包含一组密钥的Behavior
中,使用accumB
然后filter
observable2
基于Behavior
Behavior
的当前值。问题是,RxJS API中明显缺少observable2
类型。 (这就是为什么Reactive Extensions生态系统在我看来很糟糕:通过打破连续时间模型,你失去了很多FRP的可组合性。)
所以我们被迫打破FRP抽象并使用一个可变变量来保存我们之前在给定时刻看到的密钥。然后通过查看此变量中的一组键来filter
type KeyValuePair<K, V> = { key: K, val: V };
function filterByKey<K, V>(
keys: Observable<K>,
o: Observable<KeyValuePair<K, V>>
): Observable<KeyValuePair<K, V>> {
// IRL I'd build some sort of Set abstraction
// (on top of the keys of an object) to ensure
// that this scales up to big sets of keys
var seenKeys: K[] = [];
keys.subscribe(key => {
if (seenKeys.indexOf(key) === -1) {
seenKeys.push(key);
}
});
return o.filter(x => seenKeys.indexOf(x.key) !== -1);
}
。
component
(我在没有编辑器的情况下进行编码,所以我可能会有点错误,但这就是想法。)
答案 1 :(得分:-1)
不确定我是否正确解释了你想要的东西,但是看看输出你可以这样做:
const observable1 = Rx.Observable.fromArray(['key1','key3', 'key4'])
const observable2 = Rx.Observable.fromArray([
{key1: 'val1'},
{key2: 'val2'},
{key3: 'val3'},
{key4: 'val4'}
])
const observable3 = observable1
.flatMap(key => observable2.skipWhile(obj => !obj.hasOwnProperty(key)).take(1))
输出开始映射observable1
,获取每个键并跳过observable2
中的所有内容,直到找到该键。然后发射单个元素。请注意,如果键的顺序是混合的,这将无效 - 我认为如果不以更持久的方式存储对象,这是不可能的。
如果这些对象不是作为流进行异步,而是从一开始就可以使用的简单数据结构,那么使用Observables
可能对您没有好处。如果您从头开始拥有所有数据,那么您可以执行以下操作:
const keys = ['key1','key3', 'key4']
const objects = [
{key1: 'val1'},
{key2: 'val2'},
{key3: 'val3'},
{key4: 'val4'}
]
const output = objects.filter(obj => keys.includes(Object.keys(obj)[0]))
答案 2 :(得分:-1)
这与昨天的问题非常相似:RxJs: updating values in a list from an other stream identified by an ID
由于无法保证商品和订单的数量,您可以最轻松地收集observable1
和observable2
中的所有商品,然后将它们合并为一个Observable:
let observable1 = Observable.from(['key1', 'key3', 'key4']);
let observable2 = Observable.from([{id: 'key1', val: 'val1'}, {id: 'key2', val: 'val2'}, {id: 'key3', val: 'val3'}, {id: 'key4', val: 'val4'}]);
let observable3 = Observable.forkJoin(
observable1.toArray(),
observable2.toArray(),
(keys1, results1) => {
let results = [];
keys1.forEach(key => {
results1.forEach(result => {
if (key === result.id) {
results.push(result);
}
});
});
return results;
});
observable3
.subscribe(val => console.log(val));
最大的缺点是你必须在合并之前从两个Observable收集所有项目。
打印到控制台:
[ { id: 'key1', val: 'val1' },
{ id: 'key3', val: 'val3' },
{ id: 'key4', val: 'val4' } ]