按数组中的对象分组最有效的方法是什么?

例如,给定此对象数组:

[ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]

我正在表格中显示这些信息。我想通过不同的方法进行分组,但我想对值求和。

我将Undercore.js用于其groupby函数,这很有用,但并不能完成全部任务,因为我不希望它们“拆分”,而是“合并”,更像SQL groupby方法。

我要找的是能够合计特定值(如果需要)。

因此,如果我按阶段分组,我希望收到:

[
    { Phase: "Phase 1", Value: 50 },
    { Phase: "Phase 2", Value: 130 }
]

如果我组了阶段/步骤,我会收到:

[
    { Phase: "Phase 1", Step: "Step 1", Value: 15 },
    { Phase: "Phase 1", Step: "Step 2", Value: 35 },
    { Phase: "Phase 2", Step: "Step 1", Value: 55 },
    { Phase: "Phase 2", Step: "Step 2", Value: 75 }
]

是否有一个有用的脚本,或者我应该坚持使用Undercore.js,然后遍历生成的对象,自己计算总数?


当前回答

groupByArray(xs, key) {
    return xs.reduce(function (rv, x) {
        let v = key instanceof Function ? key(x) : x[key];
        let el = rv.find((r) => r && r.key === v);
        if (el) {
            el.values.push(x);
        }
        else {
            rv.push({
                key: v,
                values: [x]
            });
        }
        return rv;
    }, []);
}

这一个输出数组。

其他回答

let groupbyKeys = function(arr, ...keys) {
  let keysFieldName = keys.join();
  return arr.map(ele => {
    let keysField = {};
    keysField[keysFieldName] = keys.reduce((keyValue, key) => {
      return keyValue + ele[key]
    }, "");
    return Object.assign({}, ele, keysField);
  }).reduce((groups, ele) => {
    (groups[ele[keysFieldName]] = groups[ele[keysFieldName]] || [])
      .push([ele].map(e => {
        if (keys.length > 1) {
          delete e[keysFieldName];
        }
        return e;
    })[0]);
    return groups;
  }, {});
};

console.log(groupbyKeys(array, 'Phase'));
console.log(groupbyKeys(array, 'Phase', 'Step'));
console.log(groupbyKeys(array, 'Phase', 'Step', 'Task'));

使用ES6 Map对象:

/***@描述*采用Array<V>和分组函数,*并返回由分组函数分组的数组的Map。**@param list V类型的数组。*@param keyGetter一个函数,将数组类型V作为输入,并返回类型K的值。*K通常是V的属性键。**@返回由分组函数分组的数组的映射。*///导出函数组By<K,V>(列表:Array<V>,keyGetter:(输入:V)=>K):Map<K,Array<V>>{//const-map=new map<K,Array<V>>();函数groupBy(list,keyGetter){const-map=new map();list.forEach((项)=>{const key=keyGetter(项);constcollection=map.get(key);if(!集合){map.set(键,[项]);}其他{collection.push(项);}});回归图;}//示例用法常量宠物=[{type:“Dog”,name:“Spot”},{类型:“猫”,名称:“老虎”},{类型:“狗”,名称:“路虎”},{类型:“猫”,名称:“利奥”}];const grouped=groupBy(宠物,宠物=>宠物类型);console.log(分组.get(“Dog”));//->〔{类型:“狗”,名称:“斑点”},{类型“狗”、名称:“漫游者”}〕console.log(分组.get(“Cat”));//->〔{类型:“猫”,名称:“老虎”},{类型“猫”、名称:“狮子座”}〕const奇数=符号();const even=符号();常量=[1,2,3,4,5,6,7];const oddEven=groupBy(数字,x=>(x%2===1?奇数:偶数));console.log(oddEven.get(奇数));//->[1,3,5,7]console.log(oddEven.get(偶数));//->[2,4,6]

关于地图:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

_.groupBy([{tipo: 'A' },{tipo: 'A'}, {tipo: 'B'}], 'tipo');
>> Object {A: Array[2], B: Array[1]}

发件人:http://underscorejs.org/#groupBy

groupByArray(xs, key) {
    return xs.reduce(function (rv, x) {
        let v = key instanceof Function ? key(x) : x[key];
        let el = rv.find((r) => r && r.key === v);
        if (el) {
            el.values.push(x);
        }
        else {
            rv.push({
                key: v,
                values: [x]
            });
        }
        return rv;
    }, []);
}

这一个输出数组。

让我们生成一个通用的Array.protocol.groupBy()工具。为了多样化,让我们在递归方法上使用ES6 fancyty扩展运算符进行Haskell式模式匹配。同样,让我们让Array.prototype.groupBy()接受一个回调,该回调将项(e)、索引(i)和应用的数组(a)作为参数。

Array.prototype.groupBy=函数(cb){返回函数迭代([x,…xs],i=0,r=[[],[]]){cb(x,i,[x,…xs])?(r[0].推(x),r):(r[1].推(x),r);是否返回xs.length?迭代(xs,++i,r):r;}(本);};var arr=[0,1,2,3,4,5,6,7,8,9],res=arr.groupBy(e=>e<5);console.log(res);