【问题标题】:Combine JSON data totals合并 JSON 数据总计
【发布时间】:2018-09-19 15:40:07
【问题描述】:

我在这里有一个 Stackblitz.com 示例 https://stackblitz.com/edit/angular-xhulyo?file=app%2Fapp.component.ts,我需要合并用户总数,例如 dbrown 或 qadmin,而不是在单独的行中显示它们。它目前显示的结果如下:

User    Success Failure
dbrown  16  0
qadmin  4   0
dbrown  4   1
qadmin  21  2
administrator   42  0
cooper  8   0
ad.brown    7   0

dbrown 的总数应该是 20 和 1,qadmin 的总数应该是 25 和 2。您可以从代码中看到它只是在遍历 users 变量。这是 app.component.ts:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular 5';
  users: any[] = [];
  items: any;

  ngOnInit() {
    this.items = {"Users":{"InteractiveLogon":[{"UserName":"dbrown","Success":"16","Failed":"0"},{"UserName":"qadmin","Success":"4","Failed":"0"}],"NetworkLogon":[{"UserName":"dbrown","Success":"4","Failed":"1"},{"UserName":"qadmin","Success":"21","Failed":"2"},{"UserName":"administrator","Success":"42","Failed":"0"},{"UserName":"cooper","Success":"8","Failed":"0"},{"UserName":"ad.brown","Success":"7","Failed":"0"}]}};

    for (let user of this.items.Users.InteractiveLogon)
      this.users.push(user);

    for (let user of this.items.Users.NetworkLogon)
      this.users.push(user);
  }

}

这里是 app.component.html:

<hello name="{{ name }}"></hello>
<p>
  Start editing to see some magic happen :)
</p>
<hr>
<table *ngIf="users">
  <thead>
    <tr>
      <th align=left>User</th>
      <th>Success</th>
      <th>Failure</th>
    </tr>
  </thead>
    <tr *ngFor="let row of users" >
      <td style="padding-right: 15px">{{row.UserName}}</td>
      <td>{{row.Success}}</td>
      <td>{{row.Failed}}</td>
    </tr>
</table>

我发现了诸如 Combining JSON Data SetsSum of object properties within an array 之类的问题,它们谈论使用诸如 hasOwnProperty 和 map 和 reduce 函数之类的属性,但我不确定如何使用它们来组合具有相同名称的单独值对的总数以及如何合计这些。

编辑:

我与多个贡献者一起使用以使其更清晰(对我而言)。如果这不是 最好的 方法,欢迎发表评论。我也摆脱了“this”运算符。新的 StackBlitz here。 combineUserTotals 函数如下所示:

combineUserTotals() {
  let result: any, results: any[] = [];

  this.users.map((user) => {
    result = results.find(u => u.UserName == user.UserName);

    if (!result)
      results.push({ UserName: user.UserName, Success: Number(user.Success), Failed: Number(user.Failed) });
    else {
      result.Success += Number(user.Success);
      result.Failed += Number(user.Failed);
    }
  });

  return results;
}

【问题讨论】:

  • 我正在尝试理解您的问题。你会在 html 中只有一行代码,还是除了求和 (n+(n-1)) 之外的所有用户?
  • 不在乎。但我认为最好在打字稿代码中将用户变量中的用户组合起来。

标签: angular typescript angular5


【解决方案1】:

那么这里的 Javascript 中的酷孩子是如何做事的:

所以 redue 是一种非常有用的方式来处理数组而不使用 forloops。我知道您不知道 reduce 的工作原理,但请查看以下链接:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce https://www.w3schools.com/jsref/jsref_reduce.asp

 this.users= [...this.items.Users.NetworkLogon,...this.items.Users.InteractiveLogon];

https://stackblitz.com/edit/angular-jspxb9

现在,添加重复项:

 let result = [];
 this.users.map((a)=> {
    if (!this[a.UserName]) {
        this[a.UserName] = { UserName: a.UserName, Success: 0, Failed: 0 };
        result.push(this[a.UserName]);
    }
    this[a.UserName].Success += Number(a.Success);
    this[a.UserName].Failed += Number(a.Failed);
}, Object.create(null));
this.users = result;

【讨论】:

  • 这是汇总所有内容的好方法,与我附加的第二个链接非常相​​似,它使用了 reduce,但它并没有解决我的问题。我不需要汇总所有内容 - 我需要合并具有相同用户名的用户总数..
  • this.users= [...this.items.Users.NetworkLogon,...this.items.Users.InteractiveLogon];令人困惑。我用 this.users = this.items.Users.NetworkLogon.concat(this.items.Users.InteractiveLogon);而是
  • 没关系。它被称为扩展运算符。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。这就是时髦的孩子在 ES6+ 中的做法,哈哈。
  • 这可能是基于使用地图的最佳答案,并且没有循环遍历它,我能够正确地汇总结果。另外,感谢您提供有关如何更好地使用 JS 函数和运算符来完成此任务的指导。
  • @sfors 没问题。我们是来帮忙的。如果你有机会参加这个关于 javascript es6+ 的 udemy 课程。 udemy.com/javascript-es6-tutorial
【解决方案2】:

您可以查看用户是否在 users 数组中,如果不是,则推送它,否则将值添加到现有数组中。由于您的值是字符串,因此必须使用 parseIt() 将它们转换为求和。

for (let user of this.items.Users.InteractiveLogon) {
  let _user = this.users.find( u => u.UserName == user.UserName );
  if ( _user ){
    _user.Success = parseInt(_user.Success) + parseInt(user.Success);
    _user.Failed = parseInt(_user.Failed) + parseInt(user.Failed);
  }
  else {
    this.users.push(user);
  }
}
for (let user of this.items.Users.NetworkLogon) {
  let _user = this.users.find( u => u.UserName == user.UserName );
  if ( _user ){
    _user.Success = parseInt(_user.Success) + parseInt(user.Success);
    _user.Failed = parseInt(_user.Failed) + parseInt(user.Failed);
  }
  else {
    this.users.push(user);
  }
}

结果

User    Success Failure
dbrown  20  1
qadmin  25  2
administrator   42  0
cooper  8   0
ad.brown    7   0

【讨论】:

  • for 循环的第 2-5 行似乎没有被添加到 this.users 变量中?但这段代码似乎绝对有效!
  • 我知道它有效,因为我首先在您的 SB 中尝试过,然后将其复制到这里。我觉得代码太多了,一定有办法更简单,虽然比较难理解。我在看 c# 中的并行 fors,但打字稿中似乎没有等价物。
  • 谢谢,我喜欢你如何结合答案解决它。
【解决方案3】:

您可以通过用户名将用户添加到地图中:

const map = {};
for (let user of this.items.Users.InteractiveLogon)
  if (!map[user.UserName]) {
     map[user.UserName] = user;
  } else {
     map[user.UserName].Success += user.Success;
     map[user.UserName].Failed += user.Failed;
  }
}
for (let user of this.items.Users.NetworkLogon)
  if (!map[user.UserName]) {
     map[user.UserName] = user;
  } else {
     map[user.UserName].Success += user.Success;
     map[user.UserName].Failed += user.Failed;
  }
}

然后将您的项目添加回列表

for (let key in map) {
  if (map.hasOwnProperty(key)) {
     this.users.push(map[key]);
  }
}

【讨论】:

  • 这对 dbrown 产生了 164 的成功,对 qadmin 产生了 421 的成功,但我认为因为它将数字视为字符串。
  • 而且我似乎找不到解决问题的方法,甚至将 Number 或 parseInt 应用于 Success/Failed 属性。
  • @sfors 哈哈,我的错我没注意到。只需抛出一堆 parseInt() 就可以解决问题 ^^
猜你喜欢
  • 2020-06-30
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-17
  • 1970-01-01
  • 2018-09-23
  • 2013-04-24
相关资源
最近更新 更多