首页 > 解决方案 > RxJs 简化了重复的 pluck + flatMap

问题描述

从这样的对象开始:

const business = {
  id: "1a2b3c",
  accounts: [
    {
      name: "Pizza Express",
      stores: [
        { id: "5", webSite: "www.pizza-ny.com" },
        { id: "6", webSite: "www.pizza-la.com" }
      ]
    }
  ]
};

预期输出:商店流不是商店数组)

在此处输入图像描述

我的尝试:

return business.pipe(
  pluck("accounts"),
  flatMap(val => val),
  pluck("stores"),
  flatMap(val => val)
);

这行得通,但对我来说,运算符似乎是重复的,我想知道我是否可以简化它们。也尝试了常见的map,但我无法得到它。

例如:

return business.pipe(
  pluck("accounts"),
  map(val => val.stores)
);

我不明白为什么这会返回undefined。我得到一个数组,accounts然后投影stores每个属性......我想我误解了一些东西。

你看到更简单或更优雅的方法了吗?

标签: javascriptrxjs

解决方案


由于您正在访问嵌套数组的嵌套对象,因此您的运算符中会有一些重复。可能有一种方法可以编写一个不错的自定义运算符(如果我有时间,也许我会研究一下),但我认为最简单的方法是只使用mergeMap(与 相同的东西flatMap)。

它看起来像这样(编辑以过滤掉没有商店的帐户 - 归功于 Picci 的评论)

import { of } from "rxjs";
import { mergeMap, filter } from "rxjs/operators";

of(business)
  .pipe(
    mergeMap(b => b.accounts),
    /* filter out accounts that don't have stores */
    filter(a => !!a.stores),
    mergeMap(a => a.stores)
  )
  .subscribe(console.log);

/** console output:
 * 
 * { id: "5", webSite: "www.pizza-ny.com" }
 * { id: "6", webSite: "www.pizza-la.com" }
 */

这是堆栈闪电战:https ://stackblitz.com/edit/rxjs-njvj7q?devtoolsheight=33&file=index.ts

旁注:您甚至可以使用switchMap()而不是mergeMap(). 由于您正在处理完整的可观察对象,因此您使用哪一个之间没有太大区别。

编辑(map差异解释)

您的 map() 不工作的原因是因为您试图访问数组上的 .stores,而不是对象:

return business.pipe(
  pluck("accounts"), // accounts is an array
  map(val => val.stores) // `accounts[0].stores` would return a value for you
);

mapArray.prototype.map()和 RxJS之间的工作方式不同,map()因为 RxJS 有不同的实现。

在普通的 JavaScript 中map()是一个数组上的函数,将返回一个新的数组。关键是它始终是基于数组的。从文档(强调添加):

map()方法创建一个新数组,其中填充了对调用数组中的每个元素调用提供的函数的结果。

在 RxJS 中, map()是一个运算符,应用于Observable中的任何值。它不保证是一个数组,因此它不会迭代该值。RxJSmap()正在获取Observable内部的值并将其“映射”到新的结构/值。从文档(强调添加):

将给定project函数应用于源 Observable 发出的每个值,并将结果值作为 Observable 发出。


推荐阅读