【问题标题】:Delete properties and then sum other values in array of objects删除属性,然后对对象数组中的其他值求和
【发布时间】:2020-07-08 00:07:37
【问题描述】:

我希望能够根据对象中另一个键值对的值对对象数组中的值求和。

基于cat 的值,我想对val1, val2, val3 的值求和。

我有一个对象数组:

let json = [
      {cat: 'abc', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'abc', device: 'iphone',  site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'abc', device: 'iphone',  site: 'jeeves', val1:67, val2:78, val3:12},
      {cat: 'pqr',  device: 'ipad',  site: 'google', val1:10, val2:20, val3:30},
      {cat: 'pqr',  device: 'ipad',  site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'pqr',  device: 'ipad',  site: 'jeeves', val1:67, val2:78, val3:12},
      {cat: 'xyz',  device: 'mac',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'xyz',  device: 'mac',   site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'xyz',  device: 'mac',   site: 'jeeves', val1:67, val2:78, val3:12}
]

首先我决定删除其他 2 个字段,

我有一个包含要删除的键的数组:let arr= ['device', 'site'];

let json = [
      {cat: 'abc', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'abc', device: 'iphone',  site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'abc', device: 'iphone',  site: 'jeeves', val1:67, val2:78, val3:12},
      {cat: 'pqr',  device: 'ipad',  site: 'google', val1:10, val2:20, val3:30},
      {cat: 'pqr',  device: 'ipad',  site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'pqr',  device: 'ipad',  site: 'jeeves', val1:67, val2:78, val3:12},
      {cat: 'xyz',  device: 'mac',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'xyz',  device: 'mac',   site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'xyz',  device: 'mac',   site: 'jeeves', val1:67, val2:78, val3:12}
]

let arr= ['device', 'site'];

json.forEach(d=>{
    arr.forEach(a=>{
    delete d[a];
  });
});

console.log(json);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

有没有比 2 个forEaches 更简洁、更具扩展性的方法?我还希望能够对val1, val2, val3 的值求和。

这是我的期望值:

[
   {cat:'abc', val1:100, val2:110, val3:56},
   {cat:'pqr', val1:100, val2:110, val3:56},
   {cat:'xyz', val1:100, val2:110, val3:56}
]

【问题讨论】:

    标签: javascript jquery arrays object ecmascript-6


    【解决方案1】:

    这是 array.prototype.reduce 函数的教科书用例。它旨在遍历数组并将值组合在一起。

    const thing = json.reduce((accumulator, item) => {
      // Get the important values from the item.
      const { cat, val1, val2, val3 } = item;
    
      // If this aggregation hasn't been initialized yet, initialize it.
      if (!(cat in accumulator)) {
        accumulator[cat] = {
          cat,
          val1: 0,
          val2: 0,
          val3: 0,
        };
      }
    
      // Add the values from the current item to the accumulator.
      accumulator[cat].val1 += val1;
      accumulator[cat].val2 += val2;
      accumulator[cat].val3 += val3;
    
      // Record progress.
      return accumulator;
    }, {})
    
    // Object.values converts an object back into an array.
    console.log(Object.values(thing));
    

    【讨论】:

    • 感谢您的回复!正是我需要的。有没有办法让我在不提及 va1、val2、val3 的情况下使这种动态化?
    • 有没有办法让这个动态化?
    • 与我的想法基本相同 - 但很优雅!
    • 您可以对 Object.keys(item) 进行某种循环来定义要聚合的字段。但是,将 val1、val2 和 val3 转换为数组可能更有意义。
    • @phhu 是的,你发帖时我正在沙盒。这是要走的路。
    【解决方案2】:

    这类问题通常可以通过链接 map、reduce 和 filter 函数来处理,就像这个稍微不优雅但有效的示例:

    let json = [
          {cat: 'abc', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
          {cat: 'abc', device: 'iphone',  site: 'bing', val1:23, val2:12, val3:14},
          {cat: 'abc', device: 'iphone',  site: 'jeeves', val1:67, val2:78, val3:12},
          {cat: 'pqr',  device: 'ipad',  site: 'google', val1:10, val2:20, val3:30},
          {cat: 'pqr',  device: 'ipad',  site: 'bing', val1:23, val2:12, val3:14},
          {cat: 'pqr',  device: 'ipad',  site: 'jeeves', val1:67, val2:78, val3:12},
          {cat: 'xyz',  device: 'mac',   site: 'google', val1:10, val2:20, val3:30},
          {cat: 'xyz',  device: 'mac',   site: 'bing', val1:23, val2:12, val3:14},
          {cat: 'xyz',  device: 'mac',   site: 'jeeves', val1:67, val2:78, val3:12}
    ]
    
    const valueFields = ["val1", "val2", "val3"];
    
    const res = Object.values(
      json
      //.map(({device,site,...rest}) => ({...rest}))    // remove device & site 
      .reduce((acc,{cat,...values})=> {
        acc[cat] = acc[cat] || {cat};
        valueFields.forEach(
          (field) => (acc[cat][field] = (acc[cat][field] || 0) + values[field])
        );
        return acc;
      }, {})
    );
    
    console.log(res);

    map 通过将其余属性映射到新数组来处理设备和站点的删除。但这不是必须的,因为我们在下一步中去掉了这些,所以它被注释掉了。

    reduce 完成这项工作:对于每一行,获取它的类别并为其设置一个默认值(如果以前没有见过它)。然后将行 vals 添加到在循环的先前迭代中提取的行中。

    Object.values 用于从reduce返回的对象中获取一个数组。

    这个函数式代码不是最优雅的(改进的想法?),但它的好处是允许将操作分解为多个步骤,而不会改变原始数据。

    【讨论】:

    • 我不知道是否需要删除设备和站点。我相信提问者试图简化 reduce 函数的问题。
    • 感谢您的回复。我可以使用arr 代替devicesite 吗?我可以有更多的领域。
    • 我对代码做了一点更新,将值字段放在一个数组中,并稍微简化了代码
    • 完美!非常感谢!
    • Charles Bamford 下面的回答是对我的 JS 代码口才的研究。
    【解决方案3】:

    为了好玩(我已经在上面回答过,但我不想用不必要的深奥代码污染一个可能有用的答案)......我们可以参数化(咖喱)减少函数,使其在其他上下文中可重用,类似这样:

    let json = [
      {cat: 'abc', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'abc', device: 'iphone',  site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'abc', device: 'iphone',  site: 'jeeves', val1:67, val2:78, val3:12},
      {cat: 'pqr',  device: 'ipad',  site: 'google', val1:10, val2:20, val3:30},
      {cat: 'pqr',  device: 'ipad',  site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'pqr',  device: 'ipad',  site: 'jeeves', val1:67, val2:78, val3:12},
      {cat: 'xyz',  device: 'mac',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'xyz',  device: 'mac',   site: 'bing', val1:23, val2:12, val3:14},
      {cat: 'xyz',  device: 'mac',   site: 'jeeves', val1:67, val2:78, val3:15}  //modifed to check
    ]
    
    const sumByGroup = ({
        groupByField,
        valueFields,
        sumFunction = (acc,cur) => (acc || 0) + cur,
    }) => (
        acc,{[groupByField]:group,...values}
    ) => {
      acc[group] = acc[group] || { [groupByField]: group };
      valueFields.forEach(
        (f) => acc[group][f] = sumFunction(acc[group][f], values[f])
      );
      return acc;
    }
    
    const res = Object.values(
      json
        //.map(({device,site,...rest}) => ({...rest}))    // remove device & site
        .reduce(sumByGroup({
          groupByField: "cat", 
          valueFields:["val1", "val2", "val3"]
        }), {})
    );
    
    console.log(res);

    如果您遵循这种思路,最终可能值得使用库。 Ramda 有一个函数 reduceBy 可以完成这项工作。见live example here

    const json = [
      {cat: 'abc', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
      {cat: 'abc', device: 'iphone',  site: 'bing', val1:23, val2:12, val3:14},
      // [...]
      {cat: 'xyz',  device: 'mac',   site: 'jeeves', val1:67, val2:78, val3:15}  //modifed to check
    ]
    const reducer = (acc,cur)=>
      R.mapObjIndexed((val, key, obj) => val + cur[key] ,acc)
    ;
    const fieldsWithDefaults = {'val1':0,'val2':0,'val3':0};
    const groupFn = R.prop('cat');
    const res = R.reduceBy(reducer, fieldsWithDefaults, groupFn, json) ;
    

    【讨论】:

      【解决方案4】:

      您可以将Array#reduce 与动态键数组一起使用来求和。

      let json = [
            {cat: 'abc', device: 'iphone',   site: 'google', val1:10, val2:20, val3:30},
            {cat: 'abc', device: 'iphone',  site: 'bing', val1:23, val2:12, val3:14},
            {cat: 'abc', device: 'iphone',  site: 'jeeves', val1:67, val2:78, val3:12},
            {cat: 'pqr',  device: 'ipad',  site: 'google', val1:10, val2:20, val3:30},
            {cat: 'pqr',  device: 'ipad',  site: 'bing', val1:23, val2:12, val3:14},
            {cat: 'pqr',  device: 'ipad',  site: 'jeeves', val1:67, val2:78, val3:12},
            {cat: 'xyz',  device: 'mac',   site: 'google', val1:10, val2:20, val3:30},
            {cat: 'xyz',  device: 'mac',   site: 'bing', val1:23, val2:12, val3:14},
            {cat: 'xyz',  device: 'mac',   site: 'jeeves', val1:67, val2:78, val3:12}
      ]
      
      let arr= ['device', 'site'];
      let sumKeys = ['val1', 'val2', 'val3'];
      let sumBy = 'cat';
      let res = json.reduce((acc,curr)=>{
        let prev = acc[curr[sumBy]];
        if(prev){
          sumKeys.forEach(key=>prev[key] += curr[key]);
        } else {
          acc[curr[sumBy]] = curr;
          arr.forEach(key=>delete curr[key]);
        }
        return acc;
      }, {});
      console.log(res);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-22
        • 2021-11-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-28
        • 2013-10-14
        相关资源
        最近更新 更多