【问题标题】:Map an observable nested object to another object将一个可观察的嵌套对象映射到另一个对象
【发布时间】:2018-10-02 09:50:50
【问题描述】:

我正在尝试将来自 API 调用的响应映射到比方说 Product 对象。我似乎无法找到正确执行此操作的方法。

响应的结构如下:

{
  "data": [
    {
      "id": 1,
      "name": "keyboard",
      "details": {
        "parent": 1,
        "price": 50,
        "other": "info"
      }
    },
    {
      "id": 2,
      "name": "mouse",
      "details": {
        "parent": 2,
        "other": "info",
        "price": 20
      }
    }
  ],
  "total-records": 2,
  "more": "something"
}

无法保证响应详细信息的顺序。 (详细注意价格)。我正在尝试将上面的对象映射到一个仅包含以下属性的新对象:

{
    "id": 1,
    "name": "keyboard",
    "price": 50
},
{
    "id": 2,
    "name": "mouse",
    "price": 20
}

我正在使用 angular 和 Observables 来获取数据。现在我正在获取值,对其进行映射,并且可以获取详细信息或第一级对象属性,例如data.iddata.name。然而,获取价格等细节让我有点困惑。

我已经研究了很多关于 mergemap、flatmap 甚至 lodash 以及无数小时试图弄清楚但我似乎无法找到正确的方法来做到这一点。

我可以在地图组合中使用过滤器来实现这一点吗? 任何信息都非常感谢。

到目前为止,这是我的代码:

ngOnInit() {
    //Get by criteria
    this.productService
      .get(url, httpOptions)
      .pipe(
        map((items: Response<Product>) => {
          this.details = items["details"].filter(
            // Filter possible null values
            details=> details!==null;
          );
          this.header = items
        }),
        tap(() => console.log(this.header))
      )
      .subscribe();
  }
  
  // Header is level 1 info like name and id
  // Detail is level 2 info like price.
  // I have to have a filter on the details because sometimes 

【问题讨论】:

  • 在尝试回答之前:您没有从答案中捕获“数据”属性,所以我真的不明白您想要做什么。您将“详细信息”视为一个数组(使用过滤器),但它是一个对象。您希望将结果放入一个新对象(但没有属性),所以我想它是一个数组(产品数组?)
  • 你是对的,生成的新对象是一个产品数组

标签: angular typescript mapping observable rxjs6


【解决方案1】:

它不依赖于那里的 RxJS,而是依赖于普通的旧 Javascript。

要将您的对象映射到您期望的对象,请尝试以下操作:

this.productService
  .get(url, httpOptions)
  .pipe(
    map((items: Response<Product>) => items
      .map(item => ({ id: item.id, name: item.name, price: item.details && item.details.price || '?' }))),
    tap(mappedItems => console.log(mappedItems))
  )
  .subscribe(mappedItems => this.header = mappedItems);

我还冒昧地将您的代码移动了一点以使其更清晰(例如,在订阅中而不是直接在地图中执行某些操作)。

这段代码所做的是映射 Observable,使其返回一组新数据。

返回的新数据集也是一个映射,但是是您的实际数据的映射,而不是 Observable 的映射。

使用映射数据,您现在可以将其应用到您在订阅中创建的标题。

【讨论】:

  • 谢谢。我已经试过了。不知道为什么它说映射的项目是未定义的。但是在地图上分配新对象而不是订阅是有效的。此外,水龙头正在记录未定义。
  • 如果您的 details 对象未在您的对象中定义,您应该会遇到错误。我已经更新了我的答案以考虑到这一点。
【解决方案2】:

从 RxJS 操作符内部分配“全局”是一种奇怪的工作方式。这些 var 应该从订阅中添加。而且获取数据的方式也很奇怪。

您永远不会从响应中捕获“数据”属性,并且您将“详细信息”视为一个数组,但在您的响应中它是一个简单的对象。

按照您的 API 正在返回的内容,1 个对象至少具有 3 个属性 数据产品列表, 总记录更多...,

你可以做什么:

this.productService
  .get(url, httpOptions).pipe(
      map((res) => res.data), // extract data attribut
      map((data) => {
        return data.map((aProd) => {   //map the product and take only id, name, price
          return {
            id: aProd.id,
            name: aProd.name,
            price: aProd.details && aProd.details.price
          };
        });
      })).subscribe((productList) => {
        console.log(productList);
      });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-13
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多