Группируйте объекты по нескольким свойствам в массиве, затем суммируйте их значения

Группировка элементов в массиве по нескольким свойствам является самым близким совпадением с моим вопросом, поскольку она действительно группирует объекты по нескольким ключам в массиве. Проблема заключается в том, что это решение не суммирует значение свойств, а затем удаляет дубликаты, вместо этого он вставляет все дубликаты в двумерные массивы.

Ожидаемое поведение

У меня есть массив объектов, которые должны быть сгруппированы по 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}
];

Объекты в этом массиве считаются дублирующимися, только если их shape и color одинаковы. Если они есть, я хочу, соответственно, суммировать их used и значения instances а затем удалить дубликаты.

Таким образом, в этом примере массив результатов может содержать только четыре комбинации: square red, square blue, circle red, circle blue

проблема

Я попробовал более простой подход здесь:

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);

Ответ 1

Используйте Array # reduce со вспомогательным объектом для группировки похожих объектов. Для каждого объекта проверьте, существует ли комбинированная shape и color в помощнике. Если это не так, добавьте к помощнику с помощью Object # assign для создания копии объекта и нажмите на массив. Если да, добавьте значения в used и instances.

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);

Ответ 2

Вы можете использовать Object.values() reduce() чтобы создать один объект уникальной shape|color свойства 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)

Ответ 3

Вы можете использовать хеш-таблицу и ключи для группировки одинаковых групп.

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; }

Ответ 4

Используйте этот метод для указания нескольких свойств:

 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];
    })
 }

Вызовите этот метод как:

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

Ответ 5

У меня есть для вас предложение. Если вы хотите сделать это проще, попробуйте библиотеку Underscore: http://underscorejs.org/

Я попытался быстро использовать его и получил правильный результат:

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/

Это решение объединяет первые данные, затем вы можете делать то, что хотите, например, вычислять данные как пожелания ypur.

Может быть, вы можете его оптимизировать, сообщите мне, если вам нужна дополнительная помощь

Ответ 6

/**
  * 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];
    });
};

Пример:

Предположим, что у нас есть массив объектов. Каждый объект содержит информацию о человеке и деньгах, которыми он обладает. Мы хотим суммировать деньги для всех лиц с одной национальностью и с тем же полом.

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} );

Результат группы:

  (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)

Итак, мы получили 6 массивов. Каждый массив сгруппирован по Country и Gender

Итерируя по каждому массиву, мы можем суммировать деньги!

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);

Ответ 7

    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....

Ответ 8

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];
    });
}