如何在JavaScript中对对象数组进行分组或合并?

14

例如,我有一个对象数组如下所示。

{name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0}
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}
{name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1}
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}

我想把它们分组,并将其中所有的值相加。例如,最终结果会像这样:

{name: "Mc Donald", quantity: 8, maleCount: 1, femaleCount: 1}
{name: "KFC", quantity: 54, maleCount: 3, femaleCount: 3}

如何在JavaScript中实现这个?

我曾试图在网上找到一些解决方案,但都不是我想要的。例如,这个

8个回答

15
你可以使用数组 reduce 方法:

var arr = [
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
];

var finalArr = arr.reduce((m, o) => {
    var found = m.find(p => p.name === o.name);
    if (found) {
        found.quantity += o.quantity;
        found.maleCount += o.maleCount;
        found.femaleCount += o.femaleCount;
    } else {
        m.push(o);
    }
    return m;
}, []);

console.log(finalArr);


只是注意,这个解决方案将修改一些原始对象。 - Prestaul

8
你可以使用forEach()循环并添加到新数组。你可以将空对象作为thisArg参数传递,这样你就可以在回调函数中使用它作为this,在第二个 forEach 上下文中this仍然与第一个回调函数中的相同,因为使用了箭头函数。

var data = [{"name":"Mc Donald","quantity":4,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"Mc Donald","quantity":4,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1}]
var result = [], keys = ['quantity', 'maleCount', 'femaleCount']

data.forEach(function(e) {
  if(!this[e.name]) result.push(this[e.name] = e);
  else keys.forEach(k => this[e.name][k] += e[k])
}, {})

console.log(result)


1
使用 this 需要更多的解释,请。 - Bergi

7
你可以使用哈希表进行分组,并使用一个包含可变值的数组作为键。

var array = [{ name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }],
    groups = Object.create(null),
    result = array.reduce(function (r, o) {
        var values = ['quantity', 'maleCount', 'femaleCount'];
        if (!groups[o.name]) {
            groups[o.name] = { name: o.name };
            r.push(groups[o.name]);
            values.forEach(function (k) { groups[o.name][k] = 0; });
        }
        values.forEach(function (k) { groups[o.name][k] += o[k]; });
        return r;
    }, []);

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

使用linq.js

var array = [{ name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }],
    result = Enumerable.From(array)
        .GroupBy(
            "$.name",
            null,
            "{ name: $.Key, quantity: $$.Sum('$.quantity'), maleCount: $$.Sum('$.maleCount'), femaleCount: $$.Sum('$.femaleCount') }"
        )
        .ToArray();

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>


1
有没有理由使用groups = Object.create(null)而不是简单地使用groups = {} - Rick Hitchcock
2
如果您的名称类似于“toString”,则可能会在原型中获得错误的结果。 - Nina Scholz
1
谢谢,我从没想过那个。 - Rick Hitchcock

2

我的解决方案基于reduce:

let data = [
  {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
];

// We will only keep an array of values of the merged object
let result = Object.values(
  // Merge data to name indexed objects
  data.reduce((p,c) => {
    // Ensure key (name) exists
    if (!p[c.name]) {
       // If first occurence, duplicate object (to avoid modification of original array)
       p[c.name] = Object.assign({}, c);
    } else {
       // If key (name) already exists, sum up relevant attributes
       p[c.name].quantity += c.quantity;
       p[c.name].maleCount += c.maleCount;
       p[c.name].femaleCount += c.femaleCount;
    }
    // return updated object
    return p;
  }, {})
);

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

希望这可以帮到你


1

More functional/universal approach

const normalizeValue = (value) => value === undefined ? 0 : value
const sumUpProps = (props = []) => (a = {}, b = {}) => {
  const result = {}
  
  props.forEach((prop) => {
    result[prop] = normalizeValue(a[prop]) + normalizeValue(b[prop])
  })

  return result
}

const data = [
  {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
]

const entries = {}
const sumObjects = sumUpProps(['quantity', 'maleCount', 'femaleCount'])

data.forEach((item) => {
  const entry = entries[item.name] || {}

  entries[item.name] = sumObjects(entry, item)
})


console.log(entries)


1
这里有一个通用的解决方案,使用干净和现代的JavaScript:

function groupRestaurants(restaurants) {
    const groups = new Map();
    restaurants.forEach(restaurant => {
        const group = findOrCreateGroup(groups, restaurant.name);
        addRestaurantToGroup(group, restaurant);
    });
    return [...groups.values()];
}

function findOrCreateGroup(groups, name) {
    let group = groups.get(name);
    if (!group) {
        group = { name };
        groups.set(name, group);
    }
    return group;
}

function addRestaurantToGroup(group, restaurant) {
    for (const [key, value] of Object.entries(restaurant)) {
        if (key !== 'name') {
            if (group.hasOwnProperty(key)) {
                group[key] += value;
            } else {
                group[key] = value;
            }
        }
    }
}

const restaurants = [
    { name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 },
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 },
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 },
    { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 },
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 },
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 },
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 },
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }
];

console.log(groupRestaurants(restaurants));


1

请检查下面的代码:

var array = [
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
];

var result = [];

for(var index in array){
    var obj = array[index];
    if(result.hasOwnProperty(obj.name)) {
        result[obj.name].quantity += obj.quantity;
        result[obj.name].maleCount += obj.maleCount;
        result[obj.name].femaleCount += obj.femaleCount;
    }else{
        result[obj.name] = {};
        result[obj.name].quantity = obj.quantity;
        result[obj.name].maleCount = obj.maleCount;
        result[obj.name].femaleCount = obj.femaleCount;
    }
}

console.log(result);

1

Lodash让你的生活变得容易。

_.map(_.groupBy(yourArrayHere, 'name'), (groupMembers, groupKey) => {
    let sumObject = {name: groupKey};

    _.forEach(['quantity', 'maleCount', 'femaleCount'], (property) => {
        sumObject[property] = 0;
        _.forEach(groupMembers, (member) => {
            sumObject[property] += member[property];
        });
    });

    return sumObject;
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接