Объединить объекты с одинаковым идентификатором, но суммировать значения объектов

Я хочу уменьшить свой массив объектов, сравнивая предыдущий и текущий объект из массива, если идентификатор предыдущего объекта отличается от текущего объекта, тогда я записываю предыдущий объект в свой список результатов и заменяю его текущим объектом, иначе я суммирую значения обоих объектов. В итоге это должен быть уменьшенный массив, без дубликатов.

У меня есть такие данные:

[{
    Clicks: 210,
    Company: "A",
    _id: { CompanyID: 5 }
},
{
    Clicks: 35,
    Company: "C",
    _id: { CompanyID: 3 }
},
{
    Clicks: 15,
    Company: "B",
    _id: { CompanyID: 2 }
},
{
    Clicks: 13,
    Company: "A",
    _id: { CompanyID: 5 }
}]

И хочу свести его к такому виду:

[{
    Clicks: 223,
    Company: "A",
    _id: { CompanyID: 5 }
},
{
    Clicks: 35,
    Company: "C",
    _id: { CompanyID: 3 }
},
{
    Clicks: 15,
    Company: "B",
    _id: { CompanyID: 2 }
}]

Вот мое не правильно работающее решение:

$scope.reduce = function () {
    var result = [];
    var prev = null;

    angular.forEach($scope.data, function (value, key) {
        if (prev != null) {
            if (prev._id.CompanyID != value._id.CompanyID) {
                result.push(prev);
                prev = value;
            } else {
                prev.Clicks += value.Clicks;
            }
        } else {
            prev = value;
        }
    });
}

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


person Rep    schedule 02.06.2017    source источник


Ответы (2)


Вы можете использовать параметр thisArg в цикле forEach и передать пустой объект для хранения значений.

var data = [{"Clicks":210,"Company":"A","_id":{"CompanyID":5}},{"Clicks":35,"Company":"C","_id":{"CompanyID":3}},{"Clicks":15,"Company":"B","_id":{"CompanyID":2}},{"Clicks":13,"Company":"A","_id":{"CompanyID":5}}];
var result = [];

data.forEach(function(obj) {
  var id = obj._id.CompanyID
  if(!this[id]) result.push(this[id] = obj);
  else this[id].Clicks += obj.Clicks;
}, Object.create(null));

console.log(result);

person Nenad Vracar    schedule 02.06.2017
comment
Спасибо за помощь. Я действительно хотел знать, в чем была моя ошибка. Я уже выяснил, что сделал не так, у меня логическая ошибка, потому что я ссылаюсь на свой первый объект вместо того, чтобы делать глубокую копию. И ваш код несколько раздражает из-за использования ключевого слова this. Я думаю, что это нехорошо использовать в javascript, особенно здесь. :) - person Rep; 03.06.2017
comment
I think it is not good to use this in javascript почему это? - person Nenad Vracar; 03.06.2017
comment
В вашем примере это раздражает, но работает нормально, но с моей точки зрения это не лучший дизайн. Непонятно, что это на самом деле. Поэтому я и многие другие разработчики согласятся, что это может сбивать с толку (это в области видимости объекта или в области видимости функции объекта?) И приводить к несогласованному поведению. Вот как я изменил ваш код: ссылка - person Rep; 03.06.2017

Для версии с Array#reduce, вы можете использовать хеш-таблицу как ссылку на ту же компанию с закрытием хеш-таблицы.

var data = [{ Clicks: 210, Company: "A", _id: { CompanyID: 5 } }, { Clicks: 35, Company: "C", _id: { CompanyID: 3 } }, { Clicks: 15, Company: "B", _id: { CompanyID: 2 } }, { Clicks: 13, Company: "A", _id: { CompanyID: 5 } }],
    result = data.reduce(function (hash) {
        return function (r, a) {
            var key = a._id.CompanyID;
            if (!hash[key]) {
                hash[key] = { Clicks: 0, Company: a.Company, _id: a._id };
                r.push(hash[key]);
            }
            hash[key].Clicks += a.Clicks;
            return r;
        };
    }(Object.create(null)), []);

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

person Nina Scholz    schedule 02.06.2017