javascript - Iterating over an array of objects, summing values with the same index, and returning a new array of objects
问题描述
I have an array of objects, something like this:
const data = [ // array1
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]
],[ // array2
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]
]
What needs to be accomplished is summing x
from the array1
with x
from the array2
that have the same index. Same goes for y
and z
. The final result should be a new array of objects containing the summed values.
Something like this:
[
[{totalXOne: 2}, {totalYOne: 4}, {totalZOne: 6}],
[{totalXTwo: 2}, {totalYTwo: 4}, {totalZTwo: 6}],
[{totalXThree: 2}, {totalYthree: 4}, {totalZThree: 6}],
]
Note: All arrays are the same length, and if a value is missing it will be replaced with 0
)
I found something nice on MDN, but it's summing all x
, y
, z
values, and it's returning single summed values, like this:
let initialValue = 0;
let sum = [{x: 1}, {x:2}, {x:3}].reduce(function(accumulator,currentValue) {
return accumulator + currentValue.x;
}, initialValue)
Output:
[
[{totalX: 3}, {totalY: 6}, {totalZ: 9}], // this is not what I need
]
Is there any way I can achieve this?
UPDATE
I'm receiving JSON
from another source. It contains a property called allEmpsData
mapping over it I get the necessary salaryData
and mapping over it I'm getting the NET|GROSS|TAX data.
let allReports = [];
setTimeout(() => {
allEmpsData.map(x => {
let reports = {};
let years = [];
let months = [];
let netArr = [];
let grossArr = [];
let mealArr = [];
let taxArr = [];
let handSalaryArr = [];
x.salaryData.map(y => {
years.push(y.year);
months.push(y.month);
netArr.push(y.totalNetSalary);
grossArr.push(y.bankGrossSalary);
mealArr.push(y.bankHotMeal);
taxArr.push(y.bankContributes);
handSalaryArr.push(y.handSalary);
})
reports.year = years;
reports.month = months;
reports.net = netArr;
reports.gross = grossArr;
reports.meal = mealArr;
reports.taxesData = taxArr;
reports.handSalaryData = handSalaryArr;
allReports.push(Object.assign([], reports));
});
}, 1000);
As I can tell, everything is working as it should, but the truth is,. I don't know any better. Then here goes the magic:
setTimeout(() => {
result = allReports.reduce((r, a) =>
a.map((b, i) =>
b.map((o, j) =>
Object.assign(...Object
.entries(o)
.map(([k, v]) => ({ [k]: v + (getV(r, [i, j, k]) || 0) }))
)
)
),
undefined
);
console.log(result);
}, 1500);
... and it returns an empty array in the node console, but if I console.log
any other property from the updated code above, it's there. Any suggestions?
解决方案
这是一种使用中间 ES6 的函数式编程方式Map
:
const data = [[[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}]], [[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}],[{x: 1}, {y:2}, {z:3}]]];
const result = data[0].map( (arr, i) => Array.from(data.reduce( (acc, grp) => (
grp[i].forEach( o =>
Object.entries(o).forEach( ([k, v]) => acc.set(k, (acc.get(k) || 0) + v))
), acc
), new Map), ([k, v]) => ({ [k]: v })) );
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
解释
为了便于解释,让我们就一些条款达成一致:
我们有输入(一个数组),由groups组成。每个组都是由rows组成的数组。每行由对象组成,每个对象都有一个属性/值对。
输出没有组级别,但它有rows,同样由objects组成,每个都有一个属性/值对。
因此,使用这些术语让我们看一下代码:
由于输出数组中的行数等于任何组中的行数,因此映射第一组的行似乎是一个好的开始,即 like data[0].map
。
对于输出中的每一行,我们需要求和,并且reduce
是该工作的一个很好的候选函数,所以我们调用data.reduce
. 对于该reduce
调用的初始值,我传递了一个空的Map
. 目的是用 key-sum 对填充该 Map。稍后我们可以将该 Map 分解为单独的对象,每个对象仅具有这些键/总和对之一(但这是稍后使用的)。
因此,reduce
从 a 开始Map
并遍历组。我们需要从每个组中取出第i行来找到必须“添加”的对象。所以我们采取行grp[i]
。
对于该行中的每个对象,我们使用 . 获取属性名称和值Object.entries(o)
。事实上,该函数返回一个数组,因此我们在forEach
知道实际上只会迭代一次的情况下对其进行迭代,因为实际上只有一个属性。现在我们有了 key ( k
) 和 value v
。我们处于输入结构的最深层次。这里我们调整累加器。
acc.get(k)
我们可以知道我们已经为特定键(例如“x”)积累了什么。如果我们还没有任何东西,它会通过做 0 初始化|| 0
。然后我们将当前值添加v
到它,并将该总和存储回 Map with acc.set(k, ....)
。使用逗号运算符,我们将其acc
返回到reduce
实现(我们可以在return
这里使用,但逗号运算符更简洁)。
所以 Map 得到每个键的所有总和。通过Array.from
我们可以迭代每个键/和对,并使用回调参数,将该对转换为适当的小对象(使用{ [k]: v }
)。该[k]
表示法在 ES6 中也是一个新奇事物——它允许在对象字面量中使用动态键名。
所以...Array.from
返回一个小对象数组,每个对象都有一个总和。该数组表示要输出的一行。该map
方法创建输出中所需的所有行。
推荐阅读
- .net - 如何使用 IO.File AppendAllLines
- javascript - PHP 表上的弹出窗口
- javascript - 在 forEach 循环中运行猫鼬保存查询
- javascript - 将静态可继承属性动态添加到类
- vb.net - 通过 Sub Function 动态检查 ToolStripMenuItem 中的项目
- c++ - 英特尔 C++ 编译器英特尔 Parallel XE 19.1 提供来自 Visual Studio 代码的随机错误,以包含
或者 - bash - bash shell 的 ANSI-C 引用中的“\E”转义是什么?
- wcf - WCF REST 格式输出
- postgresql - 如何确保使用 ef core 和 postgres 释放锁?
- sql - 如何让我的 DataNavigator 在新行 INSERT 后计算新行数?