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

例如,给定此对象数组:

[ 
    { 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,然后遍历生成的对象,自己计算总数?


当前回答

基于@Ceasar Bautista的原始想法,我修改了代码并使用typescript创建了一个groupBy函数。

static groupBy(data: any[], comparator: (v1: any, v2: any) => boolean, onDublicate: (uniqueRow: any, dublicateRow: any) => void) {
    return data.reduce(function (reducedRows, currentlyReducedRow) {
      let processedRow = reducedRows.find(searchedRow => comparator(searchedRow, currentlyReducedRow));

      if (processedRow) {
        // currentlyReducedRow is a dublicateRow when processedRow is not null.
        onDublicate(processedRow, currentlyReducedRow)
      } else {
        // currentlyReducedRow is unique and must be pushed in the reducedRows collection.
        reducedRows.push(currentlyReducedRow);
      }

      return reducedRows;
    }, []);
  };

此函数接受一个回调(比较器)和一个第二个回调(onDuplicate),该回调比较行并查找副本。

用法示例:

data = [
    { name: 'a', value: 10 },
    { name: 'a', value: 11 },
    { name: 'a', value: 12 },
    { name: 'b', value: 20 },
    { name: 'b', value: 1 }
  ]

  private static demoComparator = (v1: any, v2: any) => {
    return v1['name'] === v2['name'];
  }

  private static demoOnDublicate = (uniqueRow, dublicateRow) => {
    uniqueRow['value'] += dublicateRow['value'];    
  };

使命感

groupBy(data, demoComparator, demoOnDublicate) 

将执行计算值和的分组。

{name: "a", value: 33}
{name: "b", value: 21}

我们可以根据项目的需要创建任意多个回调函数,并根据需要聚合这些值。在一个例子中,我需要合并两个数组,而不是求和数据。

其他回答

解释相同的代码。喜欢它我喜欢这里

const groupBy = (array, key) => {
  return array.reduce((result, currentValue) => {
    (result[currentValue[key]] = result[currentValue[key]] || []).push(
      currentValue
    );
    console.log(result);
    return result;
  }, {});
};

USE

 let group =   groupBy(persons, 'color');

Array.prototype.groupBy=函数(groupingKeyFn){if(groupingKeyFn的类型!=='函数'){throw new Error(“groupBy将函数作为唯一参数”);}返回this。reduce((result,item)=>{let key=groupingKeyFn(项);if(!result[key])result[key]=[];result[key].push(项);返回结果;}, {});}变量a=[{type:“video”,名称:“a”},{type:“image”,名称:“b”},{type:“video”,名称:“c”},{type:“blog”,名称:“d”},{type:“video”,名称:“e”},]console.log(a.groupBy((item)=>item.type));<script src=“https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js“></script>

MDN的Array.reduce()文档中有此示例。

// Grouping objects by a property
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Grouping_objects_by_a_property#Grouping_objects_by_a_property

var people = [
  { name: 'Alice', age: 21 },
  { name: 'Max', age: 20 },
  { name: 'Jane', age: 20 }
];

function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// { 
//   20: [
//     { name: 'Max', age: 20 }, 
//     { name: 'Jane', age: 20 }
//   ], 
//   21: [{ name: 'Alice', age: 21 }] 
// }

这里有一个使用ES6的讨厌的、难以阅读的解决方案:

export default (arr, key) => 
  arr.reduce(
    (r, v, _, __, k = v[key]) => ((r[k] || (r[k] = [])).push(v), r),
    {}
  );

对于那些询问这是如何工作的人,这里有一个解释:

在这两个=>中,您可以获得免费回报Array.prototype.reduce函数最多包含4个参数。这就是为什么要添加第五个参数,这样我们就可以使用默认值在参数声明级别为组(k)创建一个廉价的变量声明。(是的,这是巫术)如果我们的当前组在上一次迭代中不存在,我们将创建一个新的空数组((r[k]||(r[k]=[]))。这将计算到最左边的表达式,换句话说,一个现有数组或一个空数组,这就是为什么在该表达式之后会立即推送,因为无论哪种方式都会得到一个数组。当有一个返回时,逗号运算符将丢弃最左边的值,返回该场景中经过调整的前一组。

更容易理解的版本是:

export default (array, key) => 
  array.reduce((previous, currentItem) => {
    const group = currentItem[key];
    if (!previous[group]) previous[group] = [];
    previous[group].push(currentItem);
    return previous;
  }, {});

编辑:

TS版本:

const groupBy = <T, K extends keyof any>(list: T[], getKey: (item: T) => K) =>
  list.reduce((previous, currentItem) => {
    const group = getKey(currentItem);
    if (!previous[group]) previous[group] = [];
    previous[group].push(currentItem);
    return previous;
  }, {} as Record<K, T[]>);

ES6基于reduce的版本,支持iteratee函数。

如果未提供iteratee函数,则工作正常:

const data=〔{id:1,得分:2},{id:1,得分:3},{id:2,得分:2},{id:2,得分:4}〕常量组=(arr,k)=>arr.reduce((r,c)=>(r[c[k]]=[…r[c[k]]||[],c],r),{});常量组By=(arr,k,fn=()=>真)=>arr.reduce((r,c)=>(fn(c[k])?r[c[k]]=[…r[c[k]]| |[],c]:null,r),{});console.log(group(data,'id'))//通过`reduce分组`console.log(groupBy(data,'id'))//如果省略了“fn”,则结果相同console.log(groupBy(data,'score',x=>x>2))//使用iteratee分组

关于OP问题:

const data=〔{阶段:“阶段1”,步骤:“步骤1”,任务:“任务1”,值:“5”},{阶段“阶段1“,步骤:”步骤1“,任务:”任务2“,值:”10“},{阶段:”阶段1“、步骤:”阶段2“,任务1“,值“15”}、{阶段”阶段1”、步骤:“阶段2”、任务:”“任务2”、值:”20“}、{阶段“2”,步骤“步骤:”“步骤1“、任务:“1”、值“25”},{阶段:“阶段2”,步骤:“步骤1”,任务:“任务2”,值:“30”},{阶段:“阶段2”,步骤:“步骤2”,任务:“任务1”,值:”35“},{阶段:”阶段2“,步骤:”步骤2“,任务:”任务2“,值::”40“}]常量组By=(arr,k)=>arr.reduce((r,c)=>(r[c[k]]=[…r[c[k]]||[],c],r),{});常量组With=(arr,k,fn=()=>真)=>arr.reduce((r,c)=>(fn(c[k])?r[c[k]]=[…r[c[k]]| |[],c]:null,r),{});console.log(groupBy(数据,'Phase'))console.log(groupWith(data,'Value',x=>x>30))//按`Value`>30分组

另一个ES6版本,它反转分组,将值用作键,将键用作分组值:

常量数据=[{A:“1”},{B:“10”}、{C:“10”}]常量组键=arr=>arr.reduce((r,c)=>(Object.keys(c).map(x=>r[c[x]]=[…r[c[x]]||[],x]),r),{});console.log(groupKeys(数据))

注意:函数以简短的形式(一行)发布,目的是为了简洁,并仅表达想法。您可以展开它们并添加其他错误检查等。