Javascript 按数组中的多个属性对对象进行分组,然后总结它们的值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46794232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:37:50  来源:igfitidea点击:

Group objects by multiple properties in array then sum up their values

javascriptarraysduplicates

提问by Hal_9100

Grouping elements in array by multiple propertiesis the closest match to my question as it indeed groups objects by multiple keys in an array. Problem is this solution doesn't sum up the properties value then remove the duplicates, it instead nests all the duplicates in a two-dimensional arrays.

按多个属性对数组中的元素进行分组与我的问题最接近,因为它确实按数组中的多个键对对象进行分组。问题是此解决方案不会总结属性值然后删除重复项,而是将所有重复项嵌套在二维数组中。

Expected behavior

预期行为

I have an array of objects which must be grouped by shapeand color.

我有一个对象数组,必须按shape和分组color

var arr = [
    {shape: 'square', color: 'red', used: 1, instances: 1},
    {shape: 'square', color: 'red', used: 2, instances: 1},
    {shape: 'circle', color: 'blue', used: 0, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 4},
    {shape: 'circle', color: 'red', used: 1, instances: 1},
    {shape: 'circle', color: 'red', used: 1, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 5},
    {shape: 'square', color: 'red', used: 2, instances: 1}
];

Objects in this array are considered duplicates only if both their shapeand colorare the same. If they are, I want to respectively sum up their usedand instancesvalues then delete the duplicates.

只有当它们shapecolor相同时,此数组中的对象才被视为重复。如果是,我想分别总结它们的usedinstances值,然后删除重复项。

So in this example result array may only contain four combinations : square red, square blue, circle red, circle blue

所以在这个例子中结果数组可能只包含四种组合:square red, square blue, circle red,circle blue

Problem

问题

I tried a simpler approach here:

我在这里尝试了一种更简单的方法:

var arr = [
    {shape: 'square', color: 'red', used: 1, instances: 1},
    {shape: 'square', color: 'red', used: 2, instances: 1},
    {shape: 'circle', color: 'blue', used: 0, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 4},
    {shape: 'circle', color: 'red', used: 1, instances: 1},
    {shape: 'circle', color: 'red', used: 1, instances: 0},
    {shape: 'square', color: 'red', used: 4, instances: 4},
    {shape: 'square', color: 'red', used: 2, instances: 2}
];

result = [];

arr.forEach(function (a) {
    if ( !this[a.color] && !this[a.shape] ) {
        this[a.color] = { color: a.color, shape: a.shape, used: 0, instances: 0 };
        result.push(this[a.color]);
    } 
    this[a.color].used += a.used;
    this[a.color].instances += a.instances;
}, Object.create(null));

console.log(result);

but it outputs

但它输出

[{shape: "square", color: "red", used: 11, instances: 9},
{shape: "circle", color: "blue", used: 4, instances: 4}]

instead of expected result:

而不是预期的结果:

[{shape: "square", color: "red", used: 5, instances: 3},
{shape: "circle", color: "red", used: 2, instances: 1},
{shape: "square", color: "blue", used: 11, instances: 9},
{shape: "circle", color: "blue", used: 0, instances: 0}]

How can I get my function to properly group the objects by shape and color ? i.e. sum up their values and remove the duplicates ?

我怎样才能让我的函数按形状和颜色正确地对对象进行分组?即总结它们的值并删除重复项?

回答by Ori Drori

Use Array#reducewith a helper object to group similar objects. For each object, check if the combined shapeand colorexists in the helper. If it doesn't, add to the helper using Object#assignto create a copy of the object, and push to the array. If it does, add it's values to usedand instances.

Array#reduce与辅助对象一起使用来对相似的对象进行分组。对于每个对象,检查组合shape和 是否color存在于助手中。如果没有,请使用Object#assign添加到助手以创建对象的副本,然后推送到数组。如果是,请将其值添加到usedinstances

var arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];

var helper = {};
var result = arr.reduce(function(r, o) {
  var key = o.shape + '-' + o.color;
  
  if(!helper[key]) {
    helper[key] = Object.assign({}, o); // create a copy of o
    r.push(helper[key]);
  } else {
    helper[key].used += o.used;
    helper[key].instances += o.instances;
  }

  return r;
}, []);

console.log(result);

If you can use ES6, you use a Mapto collect the values, and then convert it back to an array by spreadingthe Map#values:

如果你可以使用ES6,您可以使用地图收集的值,然后将其转换回一个数组蔓延地图#值

const arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];

const result = [...arr.reduce((r, o) => {
  const key = o.shape + '-' + o.color;
  
  const item = r.get(key) || Object.assign({}, o, {
    used: 0,
    instances: 0
  });
  
  item.used += o.used;
  item.instances += o.instances;

  return r.set(key, item);
}, new Map).values()];

console.log(result);

回答by Abbes Yassine

Use this method to specify multiple properties:

使用此方法指定多个属性:

 public static groupBy(array, f) {
       let groups = {};
       array.forEach(function (o) {
         var group = JSON.stringify(f(o));
         groups[group] = groups[group] || [];
         groups[group].push(o);
       });
    return Object.keys(groups).map(function (group) {
      return groups[group];
    })
 }

Call this method like:

像这样调用这个方法:

var result = Utils.groupBy(arr, function (item) {
            return [item.shape, item.color];
          });

回答by Nenad Vracar

You can use reduce()to create one object of unique shape|colorproperties and Object.values()to return array of those values.

您可以使用reduce()创建一个具有唯一shape|color属性的对象并 Object.values()返回这些值的数组。

var arr =[{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}]

var result = Object.values(arr.reduce(function(r, e) {
  var key = e.shape + '|' + e.color;
  if (!r[key]) r[key] = e;
  else {
    r[key].used += e.used;
    r[key].instances += e.instances
  }
  return r;
}, {}))

console.log(result)

回答by Nina Scholz

You could use a hash table and the keys for grouping same groups.

您可以使用哈希表和键对相同的组进行分组。

var array = [{ shape: 'square', color: 'red', used: 1, instances: 1 }, { shape: 'square', color: 'red', used: 2, instances: 1 }, { shape: 'circle', color: 'blue', used: 0, instances: 0 }, { shape: 'square', color: 'blue', used: 4, instances: 4 }, { shape: 'circle', color: 'red', used: 1, instances: 1 }, { shape: 'circle', color: 'red', used: 1, instances: 0 }, { shape: 'square', color: 'blue', used: 4, instances: 5 }, { shape: 'square', color: 'red', used: 2, instances: 1 }],
    hash = Object.create(null),
    grouped = [];
    
array.forEach(function (o) {
    var key = ['shape', 'color'].map(function (k) { return o[k]; }).join('|');
    
    if (!hash[key]) {
        hash[key] = { shape: o.shape, color: o.color, used: 0, instances: 0 };
        grouped.push(hash[key]);
    }
    ['used', 'instances'].forEach(function (k) { hash[key][k] += o[k]; });
});

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

回答by Anthony Giretti

I have a suggestion for you. If you want to make it easier to do, you try the Underscore library : http://underscorejs.org/

我有一个建议给你。如果你想让它更容易做,你可以试试 Underscore 库:http: //underscorejs.org/

I tried quickly to use it and got the right result :

我很快尝试使用它并得到了正确的结果:

var arr = [
    {shape: 'square', color: 'red', used: 1, instances: 1},
    {shape: 'square', color: 'red', used: 2, instances: 1},
    {shape: 'circle', color: 'blue', used: 0, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 4},
    {shape: 'circle', color: 'red', used: 1, instances: 1},
    {shape: 'circle', color: 'red', used: 1, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 5},
    {shape: 'square', color: 'red', used: 2, instances: 1}
];

var byshape = _.groupBy(arr, 'shape');


var bycolor = _.map(byshape, function(array) {
                                    return _.groupBy(array, 'color')
                                });


var output = [];
_.each(bycolor, function(arrayOfShape) {
    _.each(arrayOfShape, function(arrayOfColor) {
    var computedItem = {shape: "", color: "", used: 0, instances: 0};
    _.each(arrayOfColor, function(item) {
        computedItem.shape = item.shape;
      computedItem.color = item.color;
        computedItem.used += item.used;
      computedItem.instances += item.instances;
    });
    output.push(computedItem);
  });
});
console.log(output);

http://jsfiddle.net/oLyzdoo7/

http://jsfiddle.net/oLyzdoo7/

This solution groups first data, then you can do what you want after, for example, compute data as ypur wish.

该解决方案首先对数据进行分组,然后您可以执行您想要的操作,例如,按照您的意愿计算数据。

Maybe you can optimize it, let me know if you need more help

也许您可以优化它,如果您需要更多帮助,请告诉我

回答by NetReBeL

/**
  * Groups an array of objects with multiple properties.
  *
  * @param  {Array}  array: the array of objects to group
  * @param  {Array} props: the properties to groupby
  * @return {Array} an array of arrays with the grouped results
  */   
const groupBy = ({ Group: array, By: props }) => {
    getGroupedItems = (item) => {
        returnArray = [];
        let i;
        for (i = 0; i < props.length; i++) {
            returnArray.push(item[props[i]]);
        }
        return returnArray;
    };

    let groups = {};
    let i;

    for (i = 0; i < array.length; i++) {
        const arrayRecord = array[i];
        const group = JSON.stringify(getGroupedItems(arrayRecord));
        groups[group] = groups[group] || [];
        groups[group].push(arrayRecord);
    }
    return Object.keys(groups).map((group) => {
        return groups[group];
    });
};

Example:

例子:

Assume that we have an array of objects. Each object contains info about a person and the money that possess. We want to sum up the money for all persons with the same Nationality and with the same gender.

假设我们有一个对象数组。每个对象都包含有关一个人和拥有的金钱的信息。我们要汇总所有具有相同国籍和相同性别的人的钱。

const data = [
{Name: 'George', Surname: 'Best', Country: 'Great Britain', Gender: 'Male', Money:8000}, 
{Name: 'Orion', Surname: 'Papathanasiou', Country: 'Greece', Gender: 'Male', Money: 2000}, 
{Name: 'Mairy', Surname: 'Wellbeck', Country: 'Great Britain', Gender: 'Female', Money:5000}, 
{Name: 'Thanasis', Surname: 'Papathanasiou', Country: 'Greece',Gender: 'Male', Money: 3200},
{Name: 'George', Surname: 'Washington', Country: 'Great Britain', Gender: 'Male',Money:4200}, 
{Name: 'Orfeas', Surname: 'Kalaitzis', Country: 'Greece', Gender: 'Male', Money: 7643}, 
{Name: 'Nick', Surname: 'Wellington', Country: 'USA', Gender: 'Male', Money:1000}, 
{Name: 'Kostas', Surname: 'Antoniou', Country: 'Greece', Gender: 'Male', Money: 8712},
{Name: 'John', Surname: 'Oneal', Country: 'USA', Gender: 'Male', Money:98234}, 
{Name: 'Paulos', Surname: 'Stamou', Country: 'Greece',  Gender: 'Male', Money: 3422}, 
{Name: 'Soula', Surname: 'Spuropoulou', Country: 'Greece', Gender: 'Female', Money:400}, 
{Name: 'Paul', Surname: 'Pierce', Country: 'USA',  Gender: 'Male',Money: 13000},
{Name: 'Helen', Surname: 'Smith', Country: 'Great Britain', Gender: 'Female', Money:1000}, 
{Name: 'Cathrine', Surname: 'Bryant', Country: 'Great Britain', Gender: 'Female', Money: 8712},
{Name: 'Jenny', Surname: 'Scalabrini', Country: 'USA', Gender: 'Female', Money:92214}];

const groupByProperties = ['Country', 'Gender'];

Calling the function:

调用函数:

const groupResult =  groupBy( {Group: data, By: groupByProperties} );

The group result is:

分组结果如下:

  (6) [Array(2), Array(5), Array(3), Array(3), Array(1), Array(1)]
0: Array(2)
0: {Name: "George", Surname: "Best", Country: "Great Britain", Gender: "Male", Money: 8000}
1: {Name: "George", Surname: "Washington", Country: "Great Britain", Gender: "Male", Money: 4200}
length: 2
__proto__: Array(0)
1: Array(5)
0: {Name: "Orion", Surname: "Papathanasiou", Country: "Greece", Gender: "Male", Money: 2000}
1: {Name: "Thanasis", Surname: "Papathanasiou", Country: "Greece", Gender: "Male", Money: 3200}
2: {Name: "Orfeas", Surname: "Kalaitzis", Country: "Greece", Gender: "Male", Money: 7643}
3: {Name: "Kostas", Surname: "Antoniou", Country: "Greece", Gender: "Male", Money: 8712}
4: {Name: "Paulos", Surname: "Stamou", Country: "Greece", Gender: "Male", Money: 3422}
length: 5
__proto__: Array(0)
2: Array(3)
0: {Name: "Mairy", Surname: "Wellbeck", Country: "Great Britain", Gender: "Female", Money: 5000}
1: {Name: "Helen", Surname: "Smith", Country: "Great Britain", Gender: "Female", Money: 1000}
2: {Name: "Cathrine", Surname: "Bryant", Country: "Great Britain", Gender: "Female", Money: 8712}
length: 3
__proto__: Array(0)
3: Array(3)
0: {Name: "Nick", Surname: "Wellington", Country: "USA", Gender: "Male", Money: 1000}
1: {Name: "John", Surname: "Oneal", Country: "USA", Gender: "Male", Money: 98234}
2: {Name: "Paul", Surname: "Pierce", Country: "USA", Gender: "Male", Money: 13000}
length: 3
__proto__: Array(0)
4: Array(1)
0: {Name: "Soula", Surname: "Spuropoulou", Country: "Greece", Gender: "Female", Money: 400}
length: 1
__proto__: Array(0)
5: Array(1)
0: {Name: "Jenny", Surname: "Scalabrini", Country: "USA", Gender: "Female", Money: 92214}
length: 1
__proto__: Array(0)
length: 6
__proto__: Array(0)

So, we got 6 arrays. Each array is grouped by Countryand by Gender

所以,我们得到了 6 个数组。每个数组都按Country和按分组Gender

Iterating over each array, we can sum up the money!

遍历每个数组,我们可以总结出钱!

const groupBy = ({ Group: array, By: props }) => {
    getGroupedItems = (item) => {
        returnArray = [];
        let i;
        for (i = 0; i < props.length; i++) {
            returnArray.push(item[props[i]]);
        }
        return returnArray;
    };

    let groups = {};
    let i;

    for (i = 0; i < array.length; i++) {
        const arrayRecord = array[i];
        const group = JSON.stringify(getGroupedItems(arrayRecord));
        groups[group] = groups[group] || [];
        groups[group].push(arrayRecord);
    }
    return Object.keys(groups).map((group) => {
        return groups[group];
    });
};
 
 
const data = [
 {Name: 'George', Surname: 'Best', Country: 'Great Britain', Gender: 'Male', Money:8000}, 
 {Name: 'Orion', Surname: 'Papathanasiou', Country: 'Greece', Gender: 'Male', Money: 2000}, 
    {Name: 'Mairy', Surname: 'Wellbeck', Country: 'Great Britain', Gender: 'Female', Money:5000}, 
 {Name: 'Thanasis', Surname: 'Papathanasiou', Country: 'Greece',Gender: 'Male', Money: 3200},
 {Name: 'George', Surname: 'Washington', Country: 'Great Britain', Gender: 'Male',Money:4200}, 
 {Name: 'Orfeas', Surname: 'Kalaitzis', Country: 'Greece', Gender: 'Male', Money: 7643}, 
    {Name: 'Nick', Surname: 'Wellington', Country: 'USA', Gender: 'Male', Money:1000}, 
 {Name: 'Kostas', Surname: 'Antoniou', Country: 'Greece', Gender: 'Male', Money: 8712},
 {Name: 'John', Surname: 'Oneal', Country: 'USA', Gender: 'Male', Money:98234}, 
 {Name: 'Paulos', Surname: 'Stamou', Country: 'Greece',  Gender: 'Male', Money: 3422}, 
    {Name: 'Soula', Surname: 'Spuropoulou', Country: 'Greece', Gender: 'Female', Money:400}, 
 {Name: 'Paul', Surname: 'Pierce', Country: 'USA',  Gender: 'Male',Money: 13000},
 {Name: 'Helen', Surname: 'Smith', Country: 'Great Britain', Gender: 'Female', Money:1000}, 
 {Name: 'Cathrine', Surname: 'Bryant', Country: 'Great Britain', Gender: 'Female', Money: 8712},
 {Name: 'Jenny', Surname: 'Scalabrini', Country: 'USA', Gender: 'Female', Money:92214}];
  
const groupByProperties = ['Country', 'Gender'];
const groupResult =  groupBy( {Group: data, By: groupByProperties} );

console.log(groupResult);

回答by Alfa Bravo

ES6 answers as requisted by user:

用户要求的 ES6 回答:

// To call this function:
// const result = this.toolBox.multipleGroupByArray(
//    dataArray, (property: IProperty) => [property.prop1, property.prop2, property.prop3]);

multipleGroupByArray(dataArray, groupPropertyArray) {
    const groups = {};
    dataArray.forEach(item => {
        const group = JSON.stringify(groupPropertyArray(item));
        groups[group] = groups[group] || [];
        groups[group].push(item);
    });
    return Object.keys(groups).map(function(group) {
        return groups[group];
    });
}

回答by Umair Hamid

If you want groupBy keys as per conditional fields then here is the modification for @Abbes answer:

如果您想要 groupBy 键作为条件字段,那么这里是@Abbes 答案的修改:

function groupBy(array, f) {
    let groups = {};
    array.forEach((o) => {
        var group = f(o).join('-');
        groups[group] = groups[group] || [];
        groups[group].push(o);
    });
    return groups;
}

And use it like:

并像这样使用它:

groupBy(connectedServers, (item) => {
            return [item.key1, item.key2];
});

回答by mahendren

    var arr = [
        {shape: 'square', color: 'red', used: 1, instances: 1},
        {shape: 'square', color: 'red', used: 2, instances: 1},
        {shape: 'circle', color: 'blue', used: 0, instances: 0},
        {shape: 'square', color: 'blue', used: 4, instances: 4},
        {shape: 'circle', color: 'red', used: 1, instances: 1},
        {shape: 'circle', color: 'red', used: 1, instances: 0},
        {shape: 'square', color: 'blue', used: 4, instances: 5},
        {shape: 'square', color: 'red', used: 2, instances: 1}
    ];


    result = [];

    arr.forEach(function (a) {
        if ( !this[a.color] && !this[a.shape] ) {
            this[a.color] = { color: a.color, shape: a.shape, used: 0, instances: 0 };
            result.push(this[a.color]);
        } 
        this[a.color].used += a.used;
        this[a.color].instances += a.instances;
    }, Object.create(null));

    console.log(result);


**Output:**
    [
  {
    "color": "red",
    "shape": "square",
    "used": 11,
    "instances": 9
  },
  {
    "color": "blue",
    "shape": "circle",
    "used": 4,
    "instances": 4
  }
]


    thats all perfetcly working.

     Enjoy your coding....