【问题标题】:RxJs lookup data from one array and map into anotherRxJs 从一个数组中查找数据并映射到另一个数组
【发布时间】:2022-09-27 11:05:57
【问题描述】:

我有一个 Angular 项目,它模仿了一个简单的电子商务用例,包括订单和产品。我有一个返回 Order 对象列表的 Order 服务。该界面看起来像这样:

export interface Order {
  id: string;
  products: OrderProduct[];
}

OrderProducts 看起来像这样:

export interface OrderProduct {
  productId: string;
  quantity: number;
  price: number;
}

最后一个值得注意的接口是我的Product,它有我感兴趣的name字段。它看起来像这样:

export interface Product {
  id: string;
  name: string;
  price: number;
}

现在回到 Angular,当我进入我的 OrderDetail 组件时,我想生成一个小收据或发票,它显示带有实际产品名称的行项目(又名 OrderProducts),而不仅仅是 ID。为此,我必须从ProductService 查找产品。我不想为每个产品单独拨打服务电话。相反,我宁愿抓取所有产品,并将它们保存在内存中,然后当我迭代我的 Order 的 OrderProducts 时,只需扩展该对象以包含产品的名称,如从我的本地 allProducts$ 可观察数组。这是我正在使用的sn-p:

  orderProducts$: Observable<OrderProduct[]> | undefined;
  allProducts$: Observable<Product[]> | undefined;
  constructor(
    private route: ActivatedRoute,
    private orderSvc: OrdersService,
    private productSvc: ProductService
  ) {}

  ngOnInit(): void {
    this.orderId$ = this.route.params.pipe(map((o) => o[\'orderId\']));
    this.order$ = this.orderId$.pipe(switchMap((o) => this.orderSvc.get(o)));
    this.orderProducts$ = this.order$.pipe(map((o) => o.products));
    this.allProducts$ = this.productSvc.fetch();
  }

请注意,我的所有产品都在 allProducts$ observable 中。我希望做的是创建另一个可观察对象,以某种方式将来自allProducts$ 可观察对象的相关产品与来自orderProducts$ 可观察对象的当前orderProduct 结合起来,并将其扩展为包括产品名称(同样,它仅在 allProducts$ observable 中可用。

如果我在纯 js(或 ts)中这样做,我会做这样的事情:


const productsWithName = order.products.map(op => {
    const product = allProducts.find(p => p.id === op.productId);
    return {
        ...op,
        name: product?.name || \'\',
    }
});

现在我的productsWithName 数组拥有我需要的一切。理想情况下,我可以在不离开 Observable-land 的情况下获得相同的数组,作为可观察数组(即,我不想打开我的可观察数组来将它们作为标准 JS 数组来混淆)。我已经用 RxJs 操作符尝试了这个的各种排列,但无法弄清楚。我觉得我错过了一些愚蠢的东西,但希望能有一个快速启动。首先十分感谢。

    标签: angular typescript rxjs


    【解决方案1】:

    使用combineLatest 可以解决这个问题。

    combineLatest([this.order$, this.allProducts$)).pipe(
      filter(([order, allProducts]) => !!order && !!allProducts),
      map(([order, allProducts]) => {
        return order.products.map(op => {
          const product = allProducts.find(p => p.id === op.productId);
          return {
            ...op,
            name: product?.name || '',
          }
        }) 
      }) 
    )
    

    我们可以订阅order$allProducts$ 的最新值,并在map 运算符中执行映射逻辑。 filter 过滤掉初始的 undefined 值。

    【讨论】:

      【解决方案2】:
      1. concat 两个 Observable
      2. groupByproductId 或 id
      3. o$ -> 使用 reduce 组合数据
        const orderProducts: Array<OrderProduct> = [
          { productId: '3', quantity: 2, price: 30 },
          { productId: '1', quantity: 1, price: 150 },
          { productId: '2', quantity: 9, price: 10 },
        ];
        const allProducts: Array<Product> = [
          { id: '1', name: 'Apple', price: 30 },
          { id: '2', name: 'Banana', price: 150 },
          { id: '3', name: 'Cherry', price: 10 },
        ];
        
        const orderProducts$ = from(orderProducts);
        const allProducts$ = from(allProducts);
        
        concat(orderProducts$, allProducts$).pipe(
          groupBy((k) => 'productId' in k ? k.productId : k.id),
          mergeMap((o$) => o$.pipe(reduce((a,c) => ({...a, ...c})))),
          toArray()
        ).subscribe(...);
        

        https://stackblitz.com/edit/rxjs-teu64g

      【讨论】:

        猜你喜欢
        • 2021-01-29
        • 2019-11-20
        • 1970-01-01
        • 2015-07-14
        • 2017-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-14
        相关资源
        最近更新 更多