根据具体情况加入RxJ中的两个可观察流

时间:2017-04-25 06:00:52

标签: javascript rxjs reactive-programming rxjs5

我有两个对象流,即帐户和余额。

我需要根据idaccount_id

合并(加入)两个流
var accounts = Rx.Observable.from([
    { id: 1, name: 'account 1' },
    { id: 2, name: 'account 2' },
    { id: 3, name: 'account 3' },
]);

var balances = Rx.Observable.from([
    { account_id: 1, balance: 100 },
    { account_id: 2, balance: 200 },
    { account_id: 3, balance: 300 },
]);

预期结果:

var results = [
    { id: 1, name: 'account 1', balance: 100},
    { id: 2, name: 'account 2', balance: 200},
    { id: 3, name: 'account 3', balance: 300},
];

这对RxJs是否可行?

要清楚我知道如何使用普通的js / lodash或类似的东西来做这件事。在我的情况下,我从Angular Http模块获取这些流,所以我问我是否可以在这种情况下获得RxJs的好处

2 个答案:

答案 0 :(得分:2)

根据您的一条评论,您的示例是模拟来自Angular Http调用的流。

所以而不是:

var accounts = Rx.Observable.from([
    { id: 1, name: 'account 1' },
    { id: 2, name: 'account 2' },
    { id: 3, name: 'account 3' },
]);

var balances = Rx.Observable.from([
    { account_id: 1, balance: 100 },
    { account_id: 2, balance: 200 },
    { account_id: 3, balance: 300 },
]);

我宁愿说它是:

var accounts = Rx.Observable.of([
    { id: 1, name: 'account 1' },
    { id: 2, name: 'account 2' },
    { id: 3, name: 'account 3' },
]);

var balances = Rx.Observable.of([
    { account_id: 1, balance: 100 },
    { account_id: 2, balance: 200 },
    { account_id: 3, balance: 300 },
]);

为什么: from将逐个发出每个项目,of将发出整个数组,我猜你的http响应就是整个数组。

那就是说,您可能希望实现的目标是:

const { Observable } = Rx;

// simulate HTTP requests
const accounts$ = Rx.Observable.of([
  { id: 1, name: 'account 1' },
  { id: 2, name: 'account 2' },
  { id: 3, name: 'account 3' }
]);

const balances$ = Rx.Observable.of([
  { account_id: 1, balance: 100 },
  { account_id: 2, balance: 200 },
  { account_id: 3, balance: 300 }
]);

// utils
const joinArrays = (accounts, balances) =>
  accounts
    .map(account => Object.assign({}, account, { balance: findBalanceByAccountId(balances, account.id).balance }));

const findBalanceByAccountId = (balances, id) =>
  balances.find(balance => balance.account_id === id) || { balance: 0 };

const print = (obj) => JSON.stringify(obj, null, 2)

// use forkJoin to start both observables at the same time and not wait between every request
Observable
  .forkJoin(accounts$, balances$)
  .map(([accounts, balances]) => joinArrays(accounts, balances))
  .do(rslt => console.log(print(rslt)))
  .subscribe();

输出

[
  {
    "id": 1,
    "name": "account 1",
    "balance": 100
  },
  {
    "id": 2,
    "name": "account 2",
    "balance": 200
  },
  {
    "id": 3,
    "name": "account 3",
    "balance": 300
  }
]

这是一个有效的普朗克:https://plnkr.co/edit/bc0YHrISu3FT45ftIFwz?p=preview

编辑1: 处理数组以构成结果可能不是最佳的性能,而不是返回一个数组,可能会尝试返回一个具有密钥的对象,即帐户的ID。这样您就可以删除findBalanceByAccountId函数并使用更快的应用(此处仅修改代码)

const balances$ = Rx.Observable.of({
  1: { account_id: 1, balance: 100 },
  2: { account_id: 2, balance: 200 },
  3: { account_id: 3, balance: 300 }
});

// utils
const joinArrays = (accounts, balances) =>
  accounts
    .map(account => Object.assign(
      {}, 
      account, 
      { balance: balances[account.id].balance }
    ));

答案 1 :(得分:0)

如果您确实有2个可观察量以随机顺序发出Observable<{}>的结果,则可以将它们组合起来。如果订单不是随机的,或者它们总是以“成对”的形式出现,则可以采用更有效的方式将它们组合起来。

import { from, merge } from 'rxjs';
import { map, scan, tap } from 'rxjs/operators';

const accounts = from([
  { id: 1, name: 'account 1' },
  { id: 2, name: 'account 2' },
  { id: 3, name: 'account 3' }
]);

const balances = from([
  { account_id: 1, balance: 100 },
  { account_id: 2, balance: 200 },
  { account_id: 3, balance: 300 }
]);

interface Outcome {
  id: number;
  name?: string;
  balance?: number;
}

merge<Outcome>(
  accounts,
  balances.pipe(map(a => ({ id: a.account_id, balance: a.balance })))
)
  .pipe(
    scan<Outcome>((result: Outcome[], incomming) => {
        const found = result.find(row => row.id === incomming.id);
        if (found) {
          Object.assign(found, incomming);
        } else {
          result.push(incomming);
        }
      return result;
    }, []),
    tap(r => console.log(r))
  )
  .subscribe();

请注意,结果是热烈观察。如果您只想发出单个结果并在所有结果都显示时完成,请将scan运算符替换为reduce运算符。

源代码基于RXjs版本6.您的导入版本可能与旧版本略有不同。