javascript 中的对象数组的过滤和求和
filter and sum array of objects in javascript
您好,感谢您查看我的问题。
我有一个对象数组,其中包含对应于不同日期的同一个人的多个条目。我需要对每个人的值求和。
const data = [
{
name: "Bob",
date: 3/27/22
value: 300
},
{
name: "Alice",
date: 1/13/22
value: 500
},
{
name: "Bob",
date: 5/13/22
value: 400
},
{
name: "Alice",
date: 4/19/22
value: 350
},
{
name: "John",
date: 2/15/22
value: 700
},
]
我需要的结果是:
const result = [
{
name: "Bob",
value: 700
},
{
name: "Alice",
value: 850
},
{
name: "John",
value: 700
},
]
我怎样才能以最有效的方式做到这一点?
到目前为止,我只能通过使用过滤数组方法返回名称值、将结果推送到新数组并对该数组求和来实现此目的。但是,我事先并不知道所有的名称值,所以这行不通。
感谢您的宝贵时间
一种方法可以是:
const data = [
{
name: 'Bob',
date: '3/27/22',
value: 300,
},
{
name: 'Alice',
date: '1/13/22',
value: 500,
},
{
name: 'Bob',
date: '5/13/22',
value: 400,
},
{
name: 'Alice',
date: '4/19/22',
value: 350,
},
{
name: 'John',
date: '2/15/22',
value: 700,
},
];
let res = data.reduce((agg, curr) => {
agg[curr.name] = (agg[curr.name] || 0) + curr.value;
return agg;
}, {});
const res2 = Object.keys(res).map((v) => {
return {
name: v,
value: res[v],
};
});
console.log(res2);
我认为这种方法具有合理的性能:
const data = [
{
name: "Bob",
value: 300
},
{
name: "Alice",
value: 500
},
{
name: "Bob",
value: 400
},
{
name: "Alice",
value: 350
},
{
name: "John",
value: 700
},
];
const reduceData = (data) => data.reduce((acc, cur) => {
const {name, value} = cur; // Get name and value from current item
const item = acc.find(it => it.name === name); // Find in our accumulator the desired object
item ? item.value += value : acc.push({name, value}); // Update object or create a new object if it doesn't exist
return acc; // Return accumulator
} , []);
console.log(reduceData(data));
Array.reduce 将一个内部函数作为第一个参数。
这个内部函数通常有两个参数:一个是将被函数return编辑的“累加器”,另一个是“当前数组项”。
Array.reduce 将 运行 每个数组项的内部函数,最后 return 它的值。
每次 运行 传递给内部函数的“累加器”参数是前一个内部函数调用的 return 值。
Array.reduce可以有一个额外的参数,它是传递给第一个内部函数调用的初始累加器值(这里我们使用一个空数组)。
更多信息在这里 -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
您也可以通过映射来做到这一点
const data = [
{
name: "Bob",
date: ' 3 / 27 / 22',
value: 1,
},
{
name: "Alice",
date: '1 / 13 / 22',
value: 2,
},
{
name: "Bob",
date: '5 / 13 / 22',
value: 1,
},
{
name: "Alice",
date: ' 4 / 19 / 22',
value: 2
},
{
name: "John",
date: '2 / 15 / 22',
value: 3
},
]
const res = {};
for (let i = 0; i < data.length; i++) {
if (res[data[i].name]) { //here if any key(name) is already present in res then we add the value in already present value of that name
res[data[i].name].value += data[i].value
} else {
res[data[i].name] = data[i] //here we are inserting data into res object if doesn't find any key with already name present in it
}
}
const res2 = Object.keys(res).map(person => {
return {
name: person,
value: res[person].value
}
})
console.log(res2);
您可以使用 reduce 函数合并具有相同键名 属性 的值。
const mergedData = data.reduce((prev, curr) => {
if(prev[curr.name]) {
// Sum the value if the name matches for multiple objects
prev[curr.name].value = prev[curr.name].value + curr.value
} else {
// Return the actual objects if not matches any
prev[curr.name] = curr
}
return prev
}, {});
console.log(Object.values(mergedData))
输出将是:
[
{
"name": "Bob",
"date": "3/27/22",
"value": 700
},
{
"name": "Alice",
"date": "1/13/22",
"value": 850
},
{
"name": "John",
"date": "2/15/22",
"value": 700
}
]
要了解有关 reduce 的更多信息,您可以从 documentation here
中学习
这是一次(可能有缺陷的)尝试比较此页面上答案的速度。它是否有用在很大程度上取决于正在处理的实际数据集的样子。这里的数据数组是对最坏情况的疯狂猜测(可能是错误的)。
随时fiddle使用它。
const rand_letter = () => String.fromCharCode(65+Math.floor(Math.random() * 26));
const data = Array.from({ length: 10000 }, () => ({ name: rand_letter() + rand_letter(), value: Math.floor(Math.random() * 1000) }));
const A = (data) => {
let name_map = new Map();
for(let i = 0; i < data.length; i++) {
const { name, value } = data[i];
name_map.set(name, (name_map.get(name) ?? 0) + value);
}
return [...name_map.entries()].map(([name, value]) => ({ name, value }));
};
const B = (data) => {
let name_map = {};
for(let i = 0; i < data.length; i++) {
const { name, value } = data[i];
name_map[name] = (name_map[name] ?? 0) + value;
}
return Object.entries(name_map).map(([name, value]) => ({ name, value }));
};
const C = (data) =>
Object.entries(
data.reduce((acc, { name, value }) => {
acc[name] = (acc[name] ?? 0) + value;
return acc;
}, {})
).map(([name, value]) => ({ name, value }));
const D = (data) =>
data.reduce((acc, cur) => {
const {name, value} = cur;
const item = acc.find(it => it.name === name);
item ? item.value += value : acc.push({name, value});
return acc;
}, []);
const time = (fn) => {
const iter = 100;
const t0 = performance.now();
for(let i = 0; i < iter; i++)
fn(data);
console.log('time for ' + fn.name + ' (milliseconds)', (performance.now() - t0) / iter);
};
[A, B, C, D].forEach(time);
您好,感谢您查看我的问题。
我有一个对象数组,其中包含对应于不同日期的同一个人的多个条目。我需要对每个人的值求和。
const data = [
{
name: "Bob",
date: 3/27/22
value: 300
},
{
name: "Alice",
date: 1/13/22
value: 500
},
{
name: "Bob",
date: 5/13/22
value: 400
},
{
name: "Alice",
date: 4/19/22
value: 350
},
{
name: "John",
date: 2/15/22
value: 700
},
]
我需要的结果是:
const result = [
{
name: "Bob",
value: 700
},
{
name: "Alice",
value: 850
},
{
name: "John",
value: 700
},
]
我怎样才能以最有效的方式做到这一点?
到目前为止,我只能通过使用过滤数组方法返回名称值、将结果推送到新数组并对该数组求和来实现此目的。但是,我事先并不知道所有的名称值,所以这行不通。
感谢您的宝贵时间
一种方法可以是:
const data = [
{
name: 'Bob',
date: '3/27/22',
value: 300,
},
{
name: 'Alice',
date: '1/13/22',
value: 500,
},
{
name: 'Bob',
date: '5/13/22',
value: 400,
},
{
name: 'Alice',
date: '4/19/22',
value: 350,
},
{
name: 'John',
date: '2/15/22',
value: 700,
},
];
let res = data.reduce((agg, curr) => {
agg[curr.name] = (agg[curr.name] || 0) + curr.value;
return agg;
}, {});
const res2 = Object.keys(res).map((v) => {
return {
name: v,
value: res[v],
};
});
console.log(res2);
我认为这种方法具有合理的性能:
const data = [
{
name: "Bob",
value: 300
},
{
name: "Alice",
value: 500
},
{
name: "Bob",
value: 400
},
{
name: "Alice",
value: 350
},
{
name: "John",
value: 700
},
];
const reduceData = (data) => data.reduce((acc, cur) => {
const {name, value} = cur; // Get name and value from current item
const item = acc.find(it => it.name === name); // Find in our accumulator the desired object
item ? item.value += value : acc.push({name, value}); // Update object or create a new object if it doesn't exist
return acc; // Return accumulator
} , []);
console.log(reduceData(data));
Array.reduce 将一个内部函数作为第一个参数。
这个内部函数通常有两个参数:一个是将被函数return编辑的“累加器”,另一个是“当前数组项”。
Array.reduce 将 运行 每个数组项的内部函数,最后 return 它的值。
每次 运行 传递给内部函数的“累加器”参数是前一个内部函数调用的 return 值。
Array.reduce可以有一个额外的参数,它是传递给第一个内部函数调用的初始累加器值(这里我们使用一个空数组)。
更多信息在这里 -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
您也可以通过映射来做到这一点
const data = [
{
name: "Bob",
date: ' 3 / 27 / 22',
value: 1,
},
{
name: "Alice",
date: '1 / 13 / 22',
value: 2,
},
{
name: "Bob",
date: '5 / 13 / 22',
value: 1,
},
{
name: "Alice",
date: ' 4 / 19 / 22',
value: 2
},
{
name: "John",
date: '2 / 15 / 22',
value: 3
},
]
const res = {};
for (let i = 0; i < data.length; i++) {
if (res[data[i].name]) { //here if any key(name) is already present in res then we add the value in already present value of that name
res[data[i].name].value += data[i].value
} else {
res[data[i].name] = data[i] //here we are inserting data into res object if doesn't find any key with already name present in it
}
}
const res2 = Object.keys(res).map(person => {
return {
name: person,
value: res[person].value
}
})
console.log(res2);
您可以使用 reduce 函数合并具有相同键名 属性 的值。
const mergedData = data.reduce((prev, curr) => {
if(prev[curr.name]) {
// Sum the value if the name matches for multiple objects
prev[curr.name].value = prev[curr.name].value + curr.value
} else {
// Return the actual objects if not matches any
prev[curr.name] = curr
}
return prev
}, {});
console.log(Object.values(mergedData))
输出将是:
[
{
"name": "Bob",
"date": "3/27/22",
"value": 700
},
{
"name": "Alice",
"date": "1/13/22",
"value": 850
},
{
"name": "John",
"date": "2/15/22",
"value": 700
}
]
要了解有关 reduce 的更多信息,您可以从 documentation here
中学习这是一次(可能有缺陷的)尝试比较此页面上答案的速度。它是否有用在很大程度上取决于正在处理的实际数据集的样子。这里的数据数组是对最坏情况的疯狂猜测(可能是错误的)。
随时fiddle使用它。
const rand_letter = () => String.fromCharCode(65+Math.floor(Math.random() * 26));
const data = Array.from({ length: 10000 }, () => ({ name: rand_letter() + rand_letter(), value: Math.floor(Math.random() * 1000) }));
const A = (data) => {
let name_map = new Map();
for(let i = 0; i < data.length; i++) {
const { name, value } = data[i];
name_map.set(name, (name_map.get(name) ?? 0) + value);
}
return [...name_map.entries()].map(([name, value]) => ({ name, value }));
};
const B = (data) => {
let name_map = {};
for(let i = 0; i < data.length; i++) {
const { name, value } = data[i];
name_map[name] = (name_map[name] ?? 0) + value;
}
return Object.entries(name_map).map(([name, value]) => ({ name, value }));
};
const C = (data) =>
Object.entries(
data.reduce((acc, { name, value }) => {
acc[name] = (acc[name] ?? 0) + value;
return acc;
}, {})
).map(([name, value]) => ({ name, value }));
const D = (data) =>
data.reduce((acc, cur) => {
const {name, value} = cur;
const item = acc.find(it => it.name === name);
item ? item.value += value : acc.push({name, value});
return acc;
}, []);
const time = (fn) => {
const iter = 100;
const t0 = performance.now();
for(let i = 0; i < iter; i++)
fn(data);
console.log('time for ' + fn.name + ' (milliseconds)', (performance.now() - t0) / iter);
};
[A, B, C, D].forEach(time);