繁体   English   中英

ES6 减少有条件的对象数组

[英]ES6 reduce array of objects with conditions

我陷入了(在我看来)复杂的 reduce 方法。

给定的是一个对象数组。

 const data = [ { "key": "test1", "value": 32, "type": "OUT" }, { "key": "test1", "value": 16, "type": "OUT" }, { "key": "test1", "value": 8, "type": "IN" }, { "key": "test2", "value": 32, "type": "OUT" }, { "key": "test2", "value": 16, "type": "IN" }, { "key": "test2", "value": 8, "type": "OUT" }, ];

我想获得按键属性分组的每个 object 的值总和。 有两种类型属性(IN、OUT),其中 OUT 应解释为负值。

所以在上面的例子中,我期待以下结果 object:

 //-32 - 16 + 8 = -40
 {
    "key" : "test1",
    "value" : -40,
    "type"  : "-"
  },
 //-32 + 16 - 8 = -24
 {
    "key" : "test2",
    "value" : -24,
    "type"  : "-"
  },

我正在使用SO 答案的 groupBy function 对数据进行分组。

现在我正在尝试使用带有过滤器的 reduce 来获得总和,就像在这个SO answer 中一样。

但是,它为我提供了错误的总和(16 和 8)+ 因为我使用过滤器 - 只考虑了一种类型。


这是我的代码:

 const data = [ { "key": "test1", "value": 32, "type": "OUT" }, { "key": "test1", "value": 16, "type": "OUT" }, { "key": "test1", "value": 8, "type": "IN" }, { "key": "test2", "value": 32, "type": "OUT" }, { "key": "test2", "value": 16, "type": "IN" }, { "key": "test2", "value": 8, "type": "OUT" }, ]; //group by key const groupBy = function(xs, key) { return xs.reduce(function(rv, x) { (rv[x[key]] = rv[x[key]] || []).push(x); return rv; }, {}); }; const grouped = groupBy(data,"key"); for (const [key, value] of Object.entries(grouped)) { let x = value.filter(({type}) => type === 'OUT').reduce((sum, record) => sum + record.value) console.log(x); } //const filtered = grouped.filter(({type}) => type === 'OUT'); console.log(Object.values(grouped));

问题 1:为什么 reduce 给我错误的 OUT 类型总和?

问题 2:有没有一种方法可以考虑两种类型(IN、OUT)而无需再次执行相同的过程?

如果将默认值设置为0 ,则可以在 1 reduce()中组合分组 + 计数,你可以随时添加(或删除)当前键(类型)的value

 const data = [{"key": "test1", "value": 32, "type": "OUT"}, {"key": "test1", "value": 16, "type": "OUT"}, {"key": "test1", "value": 8, "type": "IN"}, {"key": "test2", "value": 32, "type": "OUT"}, {"key": "test2", "value": 16, "type": "IN"}, {"key": "test2", "value": 8, "type": "OUT"}, ]; const res = data.reduce((p, c) => { (p[c['key']] = p[c['key']] || {...c, value: 0 }); p[c['key']].value = (c.type === 'IN')? (p[c['key']].value + c.value): (p[c['key']].value - c.value); return p; },{}); console.log(res)

Output:

{
  "test1": {
    "key": "test1",
    "value": -40,
    "type": "OUT"
  },
  "test2": {
    "key": "test2",
    "value": -24,
    "type": "OUT"
  }
}

我会把它分成两个问题:

  1. 如何减少每个数据值(reduce)
  2. 如何评估现有/新价值(切换)

这样,您的代码耦合度就会降低,并且可以为您提供更大的可扩展性。 添加一个新的运算符就像在switch中添加一个新的case一样简单。

 const reduceValue = (type, existingValue, newValue) => { switch (type) { case 'IN': return existingValue + newValue; case 'OUT': return existingValue - newValue; default: return existingValue; // or throw new Error(`Unsupported type: ${type}`) } }; const processValues = (data) => data.reduce((acc, { key, type, value }) => { acc[key]??= { key, type: '-', value: 0 }; acc[key].value = reduceValue(type, acc[key].value, value); return acc; },{}); const testData = [ { "key": "test1", "value": 32, "type": "OUT" }, { "key": "test1", "value": 16, "type": "OUT" }, { "key": "test1", "value": 8, "type": "IN" }, { "key": "test2", "value": 32, "type": "OUT" }, { "key": "test2", "value": 16, "type": "IN" }, { "key": "test2", "value": 8, "type": "OUT" } ]; console.log(processValues(testData))
 .as-console-wrapper { top: 0; max-height: 100%;important; }

我会创建 2 个函数来应用符号并将它们存储在一个变量中。

const applySign = { "IN": nr => +nr, "OUT": nr => -nr };

然后做一个简单for...of循环(使用object 解构)。 如果当前键目前没有运行总计,则将初始值设置为0 (使用nullish 合并赋值??= )。 最后将带有应用符号的当前值添加到运行总计中。

const sums = {};
for (const { key, value, type } of data) {
  sums[key] ??= 0;
  sums[key] += applySign[type](value);
}

 const data = [ { key: "test1", value: 32, type: "OUT" }, { key: "test1", value: 16, type: "OUT" }, { key: "test1", value: 8, type: "IN" }, { key: "test2", value: 32, type: "OUT" }, { key: "test2", value: 16, type: "IN" }, { key: "test2", value: 8, type: "OUT" }, ]; const applySign = { "IN": nr => +nr, "OUT": nr => -nr }; const sums = {}; for (const { key, value, type } of data) { sums[key]??= 0; sums[key] += applySign[type](value); } console.log(sums);


通过一些简单的调整,您可以更改您正在寻找的 output 中的上述内容:

const sums = {};
for (const { key, value, type } of data) {
  sums[key] ??= { key, value: 0 };
  sums[key].value += applySign[type](value);
}

const expected = Object.values(sums);

这为您提供了基本答案,尽管您期望的type属性当前缺失。 要添加它们,您必须执行另一个循环并检查最终的总和结果。

for (const sum of expected) {
  sum.type = sum.value < 0 ? "-" : "+";
}

 const data = [ { key: "test1", value: 32, type: "OUT" }, { key: "test1", value: 16, type: "OUT" }, { key: "test1", value: 8, type: "IN" }, { key: "test2", value: 32, type: "OUT" }, { key: "test2", value: 16, type: "IN" }, { key: "test2", value: 8, type: "OUT" }, ]; const applySign = { "IN": nr => +nr, "OUT": nr => -nr }; const sums = {}; for (const { key, value, type } of data) { sums[key]??= { key, value: 0 }; sums[key].value += applySign[type](value); } const expected = Object.values(sums); console.log(expected); // add type based on the value sign (don't know why) for (const sum of expected) { sum.type = sum.value < 0? "-": "+"; } console.log(expected);

如果type是 static "-"并且不应该依赖于value的符号,那么您可以在最初创建总和 object 时添加它。

sums[key] ??= { key, value: 0, type: "-" };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM