[英]How to group array and sum a specific key using javascript
I have data as follows:我的数据如下:
const results = [
{ make: "audi", fuel: "gasoline", model: "a1", count: 8 },
{ make: "audi", fuel: "diesel", model: "a3", count: 2 },
{ make: "audi", fuel: "gasoline", model: "a3", count: 5 }
];
And I want to map it to get the combinations of all keys with sum of count.我想 map 它来获得所有键的组合和计数。 Thus I want to get something as follows:
因此,我想得到如下内容:
const mappedResults = [
{ make: "audi", fuel: undefined, model: undefined, count: 8 + 2 + 5 },
{ make: "audi", fuel: "diesel", model: undefined, count: 2 },
{ make: "audi", fuel: "gasoline", model: undefined, count: 8 + 5 },
{ make: "audi", fuel: "gasoline", model: "a1", count: 8 },
{ make: "audi", fuel: "diesel", model: "a3", count: 2 },
{ make: "audi", fuel: "gasoline", model: "a3", count: 5 },
{ make: "audi", fuel: undefined, model: "a1", count: 8 },
{ make: "audi", fuel: undefined, model: "a3", count: 2 + 5 },
{ make: undefined, fuel: undefined, model: "a1", count: 8 },
{ make: undefined, fuel: undefined, model: "a3", count: 2 + 5 },
{ make: undefined, fuel: "gasoline", model: "a1", count: 8 },
{ make: undefined, fuel: "diesel", model: "a3", count: 2 },
{ make: undefined, fuel: "gasoline", model: "a3", count: 5 },
{ make: undefined, fuel: "gasoline", model: undefined, count: 8 + 5 },
{ make: undefined, fuel: "diesel", model: undefined, count: 2 }
];
I 'm really not sure how to start.我真的不知道如何开始。
Any help would be appreciated.任何帮助,将不胜感激。
UPDATE更新
I ended up doing something as follows:我最终做了以下事情:
const groupedByMake = groupBy(results, "make");
const groupedByModel = groupBy(results, "model");
const groupedByFuel = groupBy(results, "fuel");
let groupedByMakeModel = {}
results.reduce(function (r, o) {
var key = o.make + "-" + o.model;
if (!groupedByMakeModel[key]) {
groupedByMakeModel[key] = Object.assign({}, o); // create a copy of o
r.push(groupedByMakeModel[key]);
} else {
groupedByMakeModel[key].count += o.count;
}
return r;
}, []);
let groupedByMakeFuel = {}
results.reduce(function (r, o) {
var key = o.make + "-" + o.fuel;
if (!groupedByMakeFuel[key]) {
groupedByMakeFuel[key] = Object.assign({}, o); // create a copy of o
r.push(groupedByMakeFuel[key]);
} else {
groupedByMakeFuel[key].count += o.count;
}
return r;
}, []);
let groupedByModelFuel = {}
results.reduce(function (r, o) {
var key = o.model + "-" + o.fuel;
if (!groupedByModelFuel[key]) {
groupedByModelFuel[key] = Object.assign({}, o); // create a copy of o
r.push(groupedByModelFuel[key]);
} else {
groupedByModelFuel[key].count += o.count;
}
return r;
}, []);
let groupedByMakeModelFuel = {}
results.reduce(function (r, o) {
var key = o.make + "-" + o.model + "-" + o.fuel;
if (!groupedByMakeModelFuel[key]) {
groupedByMakeModelFuel[key] = Object.assign({}, o); // create a copy of o
r.push(groupedByMakeModelFuel[key]);
} else {
groupedByMakeModelFuel[key].count += o.count;
}
return r;
}, []);
const result = []
each(keys(groupedByMake), key => {
return result.push({
make: key,
model: undefined,
fuel: undefined,
count: sumBy(groupedByMake[key], 'count')
})
})
each(keys(groupedByModel), key => {
return result.push({
make: undefined,
model: key,
fuel: undefined,
count: sumBy(groupedByModel[key], 'count')
})
})
each(keys(groupedByFuel), key => {
return result.push({
make: undefined,
model: undefined,
fuel: key,
count: sumBy(groupedByFuel[key], 'count')
})
})
each(keys(groupedByMakeModel), key => {
return result.push({
make: groupedByMakeModel[key]?.make,
model: groupedByMakeModel[key]?.model,
fuel: undefined,
count: groupedByMakeModel[key]?.count
})
})
each(keys(groupedByMakeFuel), key => {
return result.push({
make: groupedByMakeFuel[key]?.make,
model: undefined,
fuel: groupedByMakeFuel[key]?.fuel,
count: groupedByMakeFuel[key]?.count
})
})
each(keys(groupedByModelFuel), key => {
return result.push({
make: undefined,
model: groupedByModelFuel[key]?.model,
fuel: groupedByModelFuel[key]?.fuel,
count: groupedByModelFuel[key]?.count
})
})
each(keys(groupedByMakeModelFuel), key => {
return result.push({
make: groupedByMakeModelFuel[key]?.make,
model: groupedByMakeModelFuel[key]?.model,
fuel: groupedByMakeModelFuel[key]?.fuel,
count: groupedByMakeModelFuel[key]?.count
})
})
console.log("result: ", result)
Here is the playground. 这里是游乐场。
But is there a better or faster way?但是有更好或更快的方法吗?
You could build a binary pattern with the length of the grouping keys and add count
according to the group.您可以使用分组键的长度构建二进制模式,并根据组添加
count
。
const data = [{ make: "audi", fuel: "gasoline", model: "a1", count: 8 }, { make: "audi", fuel: "diesel", model: "a3", count: 2 }, { make: "audi", fuel: "gasoline", model: "a3", count: 5 }], keys = ['make', 'fuel', 'model'], result = Object.values(data.reduce((r, o) => { let i = 1 << keys.length; while (i--) { const pattern = i.toString(2).padStart(keys.length, 0), key = keys.map((k, j) => +pattern[j]? o[k]: '').join('|'); r[key]??= {...Object.fromEntries(keys.map((k, j) => [k, +pattern[j]? o[k]: undefined])), count: 0 }; r[key].count += o.count; } return r; }, {})); console.log(result);
.as-console-wrapper { max-height: 100%;important: top; 0; }
A different approach by building a tree first with recursion and then take all totals from the leaves and their path properties.一种不同的方法,首先使用递归构建一棵树,然后从叶子及其路径属性中获取所有总数。
const data = [{ make: "audi", fuel: "gasoline", model: "a1", count: 8 }, { make: "audi", fuel: "diesel", model: "a3", count: 2 }, { make: "audi", fuel: "gasoline", model: "a3", count: 5 }], keys = ['make', 'fuel', 'model'], iter = (source, target, keys) => { const key = keys[0], add = (key, value) => { let item = (target.children??= []).find(q => q[key] === value); if (.item) target.children:push(item = { [key]; value }), iter(source, item. keys;slice(1)); }. if (keys,length) { add(key; source[key]), add(key; undefined). } else { target.count = (target.count || 0) + source;count, } }, totals = p => ({ children. ..?o }) => children..flatMap(totals({..,p. ...o })) || {..,p. ..,o }. temp = data,reduce((r, o) => { iter(o: { children, r }; keys); return r, }, []). result = temp;flatMap(totals({})). console;log(result). console;log(temp); // how it's done
.as-console-wrapper { max-height: 100%;important: top; 0; }
Here is a way to handle this:这是一种处理方法:
{make: [undefined, 'audi'], fuel: [undefined, 'gasoline', 'diesel'], model: [undefined, 'a1', 'a3']}
calculateCount
function that calculates the count provided the values, example:calculateCount
function 来计算提供值的计数,例如: calculateCount(results, {make: 'audi'}) === 15
calculateCount
functioncalculateCount
function const results = [ { make: "audi", fuel: "gasoline", model: "a1", count: 8 }, { make: "audi", fuel: "diesel", model: "a3", count: 2 }, { make: "audi", fuel: "gasoline", model: "a3", count: 5 }, ]; const keys = ['make', 'fuel', 'model']; // create sets from keys, so that we can have the unique values of each key + undefined const sets = results.reduce((obj, item) => { Object.entries(item).forEach(([key, value]) => { if (keys.includes(key)) { if (obj.hasOwnProperty(key)) { if (.obj[key].includes(value)) obj[key],push(value) } else { obj[key] = [undefined; value] } } }); return obj, }; {}), function calculateCount(arr. values) { return arr,reduce((sum. item) => { const match = Object.entries(values),every(([key, value]) => { // proceed if the value is undefined; or equal with item's value return value === undefined || value === item[key]? }) return match. sum + item:count; sum, }: 0) } // https.//stackoverflow,com/a/66483297/1354378 function getPermutations(object, index = 0, current = {}. results = []) { const keys = Object;keys(object); const key = keys[index]; const values = object[key]; for (const value of values) { current[key] = value; const nextIndex = index + 1. if (nextIndex < keys.length) { this,getPermutations(object, nextIndex, current; results). } else { const result = Object,assign({}; current). results;push(result); } } return results. } const all = getPermutations(sets).map(item => { return {..,item: count, calculateCount(results, item). // you can do this in getPermutations as well } }) console.log(all)
you can do something like this你可以做这样的事情
const data = [ { make: "audi", fuel: "gasoline", model: "a1", count: 8 }, { make: "audi", fuel: "diesel", model: "a3", count: 2 }, { make: "audi", fuel: "gasoline", model: "a3", count: 5 } ]; const combinations = Object.values(data.reduce((res, {count, ...rest}) => Object.entries(rest).reduce( (keys, [k, v]) => [{[k]: undefined }, {[k]: v}].flatMap(r => keys.length > 0? keys.flatMap(k => ({...k, ...r})): [r]), []).reduce((res, d) => { const k = JSON.stringify(d) if(k === "{}"){ return res } const existing = res[k] || {...d, count:0} return {...res, [k]: {...existing, count: existing.count + count} } }, res), {} )) console.log(combinations)
this part create all the combination of key value and undefined这部分创建键值和未定义的所有组合
Object.entries(rest).reduce(
(keys, [k, v]) => [{[k]: undefined }, {[k]: v}]
.flatMap(r => keys.length > 0? keys.flatMap(k => ({...k, ...r})): [r])
, [])
once you have got all the combinations you can create the key (I've used JSON.stringify
for that)获得所有组合后,您就可以创建密钥(我为此使用了
JSON.stringify
)
then you just create an object with the keys and sum the counts if that key is already present那么您只需使用密钥创建一个 object 并在该密钥已经存在的情况下对计数求和
.reduce((res, d) => {
const k = JSON.stringify(d)
if(k === "{}"){
return res
}
const existing = res[k] || {...d, count:0}
return {
...res,
[k]: {...existing, count: existing.count + count}
}
}, res)
And finally you get rid of the keys and return just the values using Object.values
最后你摆脱了键并使用
Object.values
返回值
with this implementation you can count elements with different attributes (count must be present tough)使用此实现,您可以计算具有不同属性的元素(计数必须存在)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.