【问题标题】:How to loop through an object only once in the view (single *ngFor)?如何在视图中仅循环一次对象(单个 *ngFor)?
【发布时间】:2021-06-10 22:09:54
【问题描述】:

我有这个对象,每个属性都有一个数组作为它的值。我想只循环一次这个对象,而不是使用 2 个 *ngFor 指令,因为它会导致我的应用程序运行缓慢。我也愿意用这个对象创建一个数组,但我仍然想为每个部分添加一个部分 id,如下例所示。谁能指出我正确的方向?提前非常感谢!这是我的代码。

LIVE DEMO

<div *ngFor="let group of dataObject | keyvalue">
    {{group.key}}
    <div *ngFor="let item of group.value; let i=index;">
        {{item.name}}
    </div>
</div>

注意:我的最终目标是将每个人包装到他们所属的部分中,并为每个部分添加一个 ID。 示例:

  <div id = "parentId"> 
     <div id = "section-a">
        list of all users that belong in section a
     </div>
     <div id = "section-b">
         list of all users that belong in section b
     </div>
     <div id = "section-c">
         list of all users that belong in section c
     </div>
 
    <div>
    ......
    ......
  </div>

【问题讨论】:

    标签: javascript angular typescript lodash


    【解决方案1】:

    使用*ngFor 无法做到这一点。您可以在组件中准备数据的平面版本,并提供一个 1-depth 数组以仅循环一次。

    示例应用组件:

    import { Component } from "@angular/core";
    
    @Component({
      selector: "app-root",
      templateUrl: "./app.component.html"
    })
    export class AppComponent {
      
      dataObject = {
        A: [
          { id: 1, name: "Mike", lastname: "asmith" },
          { id: 2, name: "Mary", lastname: "alters" },
          { id: 3, name: "Boris", lastname: "al" }
        ],
        B: [
          { id: 1, name: "Mike", lastname: "bmith" },
          { id: 2, name: "Mary", lastname: "balters" },
          { id: 3, name: "Boris", lastname: "bl" }
        ],
        C: [
          { id: 1, name: "Mike", lastname: "cmith" },
          { id: 2, name: "Mary", lastname: "calters" },
          { id: 3, name: "Boris", lastname: "dl" }
        ],
        D: [
          { id: 1, name: "Mike", lastname: "dmith" },
          { id: 2, name: "Mary", lastname: "dalters" },
          { id: 3, name: "Boris", lastname: "dl" }
        ],
        E: [
          { id: 1, name: "Mike", lastname: "emith" },
          { id: 2, name: "Mary", lastname: "ealters" },
          { id: 3, name: "Boris", lastname: "el" }
        ]
      };
    
      flatDataObject = [];
    
      constructor() {
        // Loop through dataObject
        Object.keys(this.dataObject).forEach(k => {
          // Assign key as a `key` attribute to first object in array.
          this.dataObject[k][0].key = k;
          // Push the arrays into one
          this.flatDataObject.push(...this.dataObject[k]);
        });
      }
    }
    

    示例 HTML:

    <div *ngFor="let group of flatDataObject">
      <!-- Check if the object has `key` attribute -->
      <div *ngIf="group.key">
        <!-- If it has `key` attribute: -->
        {{group.key}}
      </div>
      <div>
        {{group.lastname}}
      </div>
    </div>
    

    这里是演示:https://stackblitz.com/edit/angular-6-template-sk8tkm?file=src/app/app.component.html

    这是我能想到的最佳解决方案。它仍然总共有两个循环,但至少其中一个没有使用*ngFor

    【讨论】:

    • 如何为每个部分添加一个带有 id 的 div?例如,对于“A”部分及其用户,您如何将其包装在 id =“section-a”的部分中,对于“B”部分,如何将其包装在 id =“section-b”的 div 中,等等开吗?
    • @progx 您无法使用我的解决方案为整个组分配 div id,因为它的设计目的是遍历数据集的扁平版本。但是您可以根据自己的解决方案设置 div id。示例:
    • 我的解决方案?你的意思是回去使用 2 *ngFor 指令?我只想使用一个 *ngFor。
    • @progx 我回答了你的问题,而不是你的评论。当涉及到您的第二个问题时,您基本上是在创建一个 1 深度的 div 循环,例如:section-a-1, section-a-2, section-a-3, section-b-1 .... 你可以不要将它们分组并用组 ID 包装它们,因为没有父 div。如果您想这样做,请保留您的代码并使用
      更改第一个 div。
    【解决方案2】:

    您有一个 DictionaryArrays(作为数据结构),因此对于必要的迭代,您有以下时间复杂度:

    • O(n) : n 代表字典长度
    • O(m) : m 代表Array类型)的长度

    您执行迭代的总复杂度为O(mn)O(m*n) 任何地方。例如,下面的代码具有相同的时间复杂度:

    Object.entries(this.dataObject).reduce((result, entry) => {
        const [key, value] = entry;
        value.forEach((v, i) => (result[`${key}_${i}`] = v.name));
        return result;
    },{});
    

    如果你有一个限制,我认为你可以降低时间复杂度在语言方法和方便的技巧的帮助下,如下所示:

    Object.values(this.dataObject).map(m => m).flat().map(m => m.name);
    

    然后,将每个 3 项拆分:

    <div *ngFor="let item of simplified2; let i = index" [id]="findId(i)">
      {{findId(i)}} {{item}}
    </div>
    
    
    findId(index: number) {
        return Math.floor(index / 3); //if you are sure about the length of values
    }
    

    我在stackblitz 中提供了上述所有内容。你可以了解更多关于Data Structures in JavaScript: Arrays, HashMaps, and Lists

    【讨论】:

    • 我想要与我相同的输出(标题 - 例如“A”及其子元素下方),然后我想为每个部分添加 div id,例如“section-a”对于 A 部分,对于部分“B”,“section-b”,依此类推。你知道怎么做吗?
    • @progx 正如我之前所描述的,您最终将拥有与您自己的代码相同的时间复杂度来满足您的目的,即O(nm)性能问题 基于必要的迭代。想到的唯一方法是分页技术来控制性能并按需加载请求的部分
    • @progx 你想要超越计算机算法的东西!
    【解决方案3】:

    您可以生成减少的新属性并使用此新属性

    在你的组件中

    dataObject = {
        A: [
            {id: 1, name: 'Mike', lastname: 'asmith'},
            {id: 2, name: 'Mary', lastname: 'alters'},
            {id: 3, name: 'Boris', lastname: 'al'}
        ],
        B: [
            {id: 1, name: 'Lauren', lastname: 'bmith'},
            {id: 2, name: 'Travis', lastname: 'balters'},
            {id: 3, name: 'Barn', lastname: 'bl'}
        ], 
        ...
    }
    dataObjectDisp = Object.entries(this.dataObject).reduce((prev, [key, value]) =>{
      return [...prev, ...value.map((user, index) => ({ 
        ...user, parent: key, index }))]
    }, [])
    

    以上代码将结构简化为

    [
      {
        "id": 1,
        "name": "Mike",
        "lastname": "asmith",
        "parent": "A",
        "index": 0
      },
      {
        "id": 2,
        "name": "Mary",
        "lastname": "alters",
        "parent": "A",
        "index": 1
      },
      ...
    ]
    

    现在我们有一种方法可以确定新部分何时开始。它的索引为零

    要显示 html,您可以执行以下操作

    <h1>New</h1>
    <div *ngFor="let item of dataObjectDisp; let i = index" [attr.data-article]='"section" + item.parent'>
      <ng-container *ngIf="item.index === 0">
        {{ item.parent }}
      </ng-container>
      <div>
        {{item.lastname}}
      </div>
    </div>
    

    注意属性[attr.data-article]='"section" + item.parent'。这可以用来识别不同的部分

    【讨论】:

    • 您知道您的解决方案的时间复杂度是多少吗? OP 存在性能问题。
    • @Owen Kelvin 你能提供像 stackblitz 这样的现场演示吗?
    • 我想用 id 将所有部分包装在一个 div 中。例如对于 A 部分,我想添加一个 id = "sectionA" 并且它的所有子元素都应该在里面。您的解决方案为每个孩子(每个人)添加了一个“”sectionA。希望这是有道理的
    • @Owen Kelvin 我试图坚持使用单 *ngFor,而您的新解决方案使用双 *ngFor。但是,我非常感谢您的工作和努力,所以我给您额外的 100 点声望点。谢谢老哥!
    猜你喜欢
    相关资源
    最近更新 更多
    热门标签