具有一个可观察的映射到第二个可观察数据中的数据,该数据发射具有映射数据的第三个可观察数据

时间:2017-02-21 22:08:36

标签: typescript rxjs

对于专业人士来说,问题可能很简单。尝试了多种方法后,我无法绕过它。

我有一个包含密钥列表的可观察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中实现这一目标?顺便说一句,我使用的是打字稿。

3 个答案:

答案 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

由于无法保证商品和订单的数量,您可以最轻松地收集observable1observable2中的所有商品,然后将它们合并为一个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' } ]