【问题标题】:Using nested mapping within Observable subscription - Angular6在 Observable 订阅中使用嵌套映射 - Angular6
【发布时间】:2019-03-15 15:33:45
【问题描述】:

我正在通过 API 调用(在我的 dataservicse.ts 文件中)获取一些数据 - 我得到的响应是一种复杂的 JSON 结构。我可以在相应的组件文件中获取响应的特定部分,如下所示 -

这是代表一般结构的 JSON 对象响应之一 -

{
    "address": {
        "building": "1234",
        "coord": [0, 0],
        "street": "123 Main Street",
        "zipcode": "00000"
    },
    "address2": "Test Suite 000",
    "grades": [{
        "grade": "A",
        "score": 3
    }, {
        "grade": "B",
        "score": 4
    }, {
        "grade": "A",
        "score": 2
    }],
    "name": "John Doe",
    "_id": "1212121"
}

现在 - 我的目标是从每个响应对象获取“名称”属性以及等级属性中的第一个“等级”值 - 并将它们映射到单独的数组中,以便我可以使用 * 将它们显示在表格列中ngFor.

这是我的组件代码

export class TestDataComponent implements OnInit {

name$: Object;
grade$: Object;

constructor(private data: DataService, private data2: DataService) { }

ngOnInit() {
   //getAPIData is returning the API response from the dataservices.ts file
   this.data.getAPIData().subscribe(
       data=>console.log(data.response.map(name=>name.name))
       ); //this works fine - I get the names in an array

     this.data2.getAPIData().subscribe(
     data2=>console.log((data2.response.map(grade=>grade.grades)).map(grades 
        => {grades.map((value, index) => value.grade})) //this returns an undefined value
  );
 }

现在 - 如果我 console.log((data2.response.map(grade=>grade.grades)) 我得到一个数组对象数组,例如 -

Array - [Array(3), Array(3), Array(2)]

并且它们中的每一个都由对象的'grades'属性数组组成。 (从上面取第一个数组)-

Array(3)
0:{"grade": "A","score": 3}
1:{"grade": "B", "score": 4}
2:{"grade": "A", "score": 2}

因此 - 我进一步映射我的初始响应以实现“等级”值。另外-我只想要一年级-因此我添加了一个简单的条件如下-

console.log((data2.response.map(grade=>grade.grades))
              .map(grades 
                  => {grades.map((value, index) =>{if(index<1) value.grade})}))

正如评论中提到的 - 我得到一个未定义的值。

我不明白这个问题可能相当复杂,但我已尽力尽可能清楚地解释它。我的目标是从每个对象中获取第一个“等级”值并将它们显示在一个数组中 - 就像名称一样,这样我就可以使用它在表格上显示它。

我对 Angular6 还很陌生,刚刚从 Angular 1.6 切换 - 所以我很确定我搞砸了。

通过订阅中的嵌套映射将成绩值放入数组中的最佳方法是什么?或者有更好的方法吗?

另外——为了简单起见,忽略第一个订阅存在的事实(对于 name 属性)——我在这里展示它是为了清楚我想要实现的目标。

【问题讨论】:

  • 从表面上看,它与 Observable 无关,您更关心如何将您的响应映​​射到新的数据结构中对吗?
  • 是的 - 我不确定这是映射问题还是我在“订阅”中的实现方式有问题 - 不知道为什么映射不能以这种方式工作 - 我试图分开通过将每个映射存储到一个变量,它似乎做得很好。但我确实需要它,因为它在订阅内。不确定这是否有意义,如果需要,我可以提供更多详细信息。
  • 您是否两次注入相同的DataService?每次调用 getAPIData 是否都返回您发布的相同 JSON 对象?为什么要尝试在订阅中映射/减少值而不是使用管道运算符链?

标签: javascript arrays angular rxjs


【解决方案1】:

这就是我认为你所要求的,因为你从来没有给出你试图映射/减少的具体例子。此外,这是原生 JavaScript,但可以很容易地翻译成 RxJS。

// I am assuming that a call to `DataServce.getAPIData()` returns an array
const api_response = [{
    "address": {
        "building": "1234",
        "coord": [0, 0],
        "street": "123 Main Street",
        "zipcode": "00000"
    },
    "address2": "Test Suite 000",
    "grades": [{
        "grade": "A",
        "score": 3
    }, {
        "grade": "B",
        "score": 4
    }, {
        "grade": "A",
        "score": 2
    }],
    "name": "John Doe",
    "_id": "1212121"
}];

// Map and pluck the values you need
const result = api_response.map(v => [v.name, v.grades[0].score])

// Or as an array of objects
const obj_result = result.map(([name, score]) => ({ name, score }))

// Is this what you want?
console.log('Array', result);
console.log('Objects', obj_result);

快速更新

感谢您接受答案。只是想快速了解一下使用 RxJS 会是什么样子。我说 可能 因为下面的代码 sn-p 未经测试。

this.nameAndScores$ = data.getAPIData().pipe(
  map(({ response }) => response),
  map(({ name, grades }) => ( { name, score: grades[0].score } ))
)

假设nameAndScores$ 是您的组件实例的属性,您可以使用*ngFor item in nameAndScores$ | async 并避免在您的代码中进行任何显式订阅。

【讨论】:

  • 是的 - 你是对的 - 它确实返回了一个数组。但我只想获得第一个“成绩”属性 - 而不是“分数”。另外 - 这可以在订阅中完成而不声明 const - 只需 console.log 映射结果?
  • 我还是不清楚你要回什么数据结构?我已经更新了从嵌套等级数组中提取score 的答案。使用 RxJS,您通过一系列运算符“推送”数据而不是“拉”它们通过来执行相同的逻辑。您所指的const 将在运算符链中生成并通过流推送,而不是在subscribe 中将其拉入处理。
  • 嗯,这几乎是我想要的——我明白这里正在做什么,但仍然存在的问题是我有很多数据——所以响应是一个数组对象数组 [ ["John Doe", 3], ["Jeff Doe", 4], ...]- 不知道如何 *ngFor 在 元素中获取每个值显示它们。如果您愿意,我可以提供控制台显示的屏幕截图。
  • 也许截图会有所帮助。但即使是[[name, score], [name, score], ...] 的数组也可以轻松映射到[{name, score}, {name, score}, ...]。那是你需要的吗?再次更新。
  • 非常感谢!我完全迷失了 Observable 的概念,我忘记了我可以使用 vanilla JS 做到这一点 - 再次感谢您的帮助!
【解决方案2】:

您在第二个地图函数中使用了带有粗箭头的大括号。使用花括号时,您应该使用 return 关键字返回一个值。

【讨论】:

  • 你的意思是-console.log((data2.response.map(grade=>grade.grades)).map(grades => {grades.map((value, index) =>{ if(index
  • 能否添加data2.response的样本
猜你喜欢
相关资源
最近更新 更多
热门标签