如何通过属性分组数组对象?



如何按属性对数组对象进行分组。例如,下面我想按其oldStockBooks.name对样本数据进行分组(请参阅底部的预期结果)?

看过下面这些链接,但是我不能在我的场景stack-link-1, stack-link-2中应用它。

我已经尝试了下面的代码,但它并没有像预期的那样工作。

var counter = {};
oldStockBooks.forEach(function(obj) {
var key = JSON.stringify(obj)
counter[key] = (counter[key] || 0) + 1
});

样本数据:

const oldStockBooks = [
{
name: 'english book',
author: 'cupello',
version: 1,
//... more props here
},
{
name: 'biology book',
author: 'nagazumi',
version: 4,
},
{
name: 'english book',
author: 'cupello',
version: 2,
},
];

预期结果:只显示nameauthortotal道具。而total道具则是按图书名称排列的复制数。

const output = [
{
name: 'english book',
author: 'cupello',
total: 2,
},
{
name: 'biology book',
author: 'nagazumi',
total: 1,
},
];

您可以在oldStockBooks上使用reduce来构建Map对象。由于希望按name分组,Map对象中的键可以是对象中的name值。在构建Map时,如果遇到Map中已经存在的名称,可以从存储在该键处的对象中获取总数,并使用更新后的总数创建一个新对象。否则,如果还没有看到该对象,则可以将总数设置为0(通过使用默认值:total = 0进行解构来完成)。一旦你有了你的地图,你可以从它的抓取值对象,并把它变成一个数组Array.from():

const oldStockBooks = [{ name: 'english book', author: 'cupello', version: 1, }, { name: 'biology book', author: 'nagazumi', version: 4, }, { name: 'english book', author: 'cupello', version: 2, }, ];
const res = Array.from(oldStockBooks.reduce((acc, obj) => {
// Grab name, author and total keys from the seen object. If the object hasn't already been seen, use the current object to grab the name and author, and default the total to 0
const {name, author, total=0} = acc.get(obj.name) || obj;
return acc.set(obj.name, {name, author, total: total+1}); // update the total
}, new Map).values());
console.log(res);

您可以使用Map

有效地获得结果

const oldStockBooks = [
{
name: "english book",
author: "cupello",
version: 1,
},
{
name: "biology book",
author: "nagazumi",
version: 4,
},
{
name: "english book",
author: "cupello",
version: 2,
},
];
const map = new Map();
oldStockBooks.forEach(({ name, author }) =>
map.has(name)
? (map.get(name).total += 1)
: map.set(name, { name, author, total: 1 })
);
const result = [...map.values()];
console.log(result);

你接近答案:

var response = {}; // here we put the itens mapped by the key (the 'name' field)
oldStockBooks.forEach(function(obj) {
if( !response[ obj.name ] ) { // if it is the first item of this 'name'
response[ obj.name ] = {
name: obj.name,
author: obj.author,
total: 1,
};
} else { // else, we have one already, so lets only increment the total count
response[ obj.name ].total += 1;
}
});
// if need a list/array
var myBooks = [];
for(var key in response) myBooks.push( response[key] );

注意:这里假设您不关心两本书是否有相同的标题但作者不同。

const oldStockBooks = [
{
name: 'english book',
author: 'cupello',
version: 1,
},
{
name: 'biology book',
author: 'nagazumi',
version: 4,
},
{
name: 'english book',
author: 'cupello',
version: 2,
},
];
var counter = {}
oldStockBooks.forEach((obj) => {
var key = obj.name
if (!counter[key]) {
counter[key] = {
...obj,
total: 1,
}
} else {
counter[key].total = counter[key].total + 1
}
});
const result = Object.keys(counter).map(key => {
const {name, author, total} = counter[key]
return {name, author, total}
})
console.log(result);

Codepen

最新更新