使用 reduce 的数组输出的特定键的累积和

Cumulative sum of specific keys with array output using reduce

假设我有以下数组:

let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]

我想计算每个键的累计和,但我也希望输出是一个长度相同的数组,每一步都有累计值。最后的结果应该是:

[{a: 1, b: 2}, {a: 3, b: 6}, {a: 11, b: 5}]

我的问题是我无法获得所需的数组。我只得到最终的对象:

let result = arr.reduce((accumulator, element) => {
  if(accumulator.length === 0) {
    accumulator = element
  } else {
    for(let i in element){
      accumulator[i] = accumulator[i] + element[i]
    }
  }
  return accumulator
}, [])

console.log(result); // {a: 11, b: 5}

像这样:

const arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]

const result = arr.reduce((accumulator, element, index) => {
  if(accumulator.length === 0) {
    accumulator.push(element)
  } else {
    const sum = {};
    for(let i in element) {
      sum[i] = element[i] + (accumulator[index - 1][i] || 0)
    }
    accumulator.push(sum)
  }
  return accumulator
}, [])

console.log(result);

另一个选项是使用 Map 保持总和结果,如果数组元素中的键不总是相同,它会有所帮助。

const arr = [{a: 1, b: 2}, {a: 2}, {a: 8, b: -1}];
const map = new Map();
const result = arr.map((element) => {
  const sum = {};
  for (let i in element) {
    sum[i]= element[i] + (map.get(i) || 0);
    map.set(i, sum[i]);
  }
  return sum;
});

console.log(result);

这里有一些更简洁的 reduce,结果可能不那么可读...

array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])

let array = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
let culm = array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
console.log(culm)
 

您可以使用 reduce as

轻松实现结果

let arr = [
  { a: 1, b: 2 },
  { a: 2, b: 4 },
  { a: 8, b: -1 },
];

const result = arr.reduce((acc, curr, i) => {
  if (i === 0) acc.push(curr);
  else {
    const last = acc[i - 1];
    const newObj = {};
    Object.keys(curr).forEach((k) => (newObj[k] = curr[k] + last[k]));
    acc.push(newObj);
  }
  return acc;
}, []);

console.log(result);

你想要的听起来像 scan() 高阶函数(借用 ramda.js 的想法),它允许你 return 每个元素的累积结果你的阵列。 scan 方法类似于 .reduce() 方法的行为方式,不同之处在于它 return 是每个元素的累加器。您可以像这样自己构建 scan() 函数:

let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}];

const scan = ([x, ...xs], fn) => xs.reduce((acc, elem) => {
  return [...acc, fn(acc.at(-1), elem)];
}, xs.length ? [x] : []);

const res = scan(arr, (x, y) => ({a: x.a+y.a, b: x.b+y.b}));
console.log(res);

您可能会考虑进一步改进,例如为扫描方法提供初始值(类似于 reduce 接受一个值的方式)。此外,如果您需要更好的浏览器支持,.at() 方法目前的浏览器支持有限,因此您可以考虑创建自己的 at() 函数:

const at = (arr, idx) => idx >= 0 ? arr[idx] : arr[arr.length + idx];

鉴于:

const xs =
  [ {a: 1, b:  2}
  , {a: 2, b:  4}
  , {a: 8, b: -1}];

定义一个函数sum例如:

const sum = ([head, ...tail]) =>
  tail.reduce((x, y) =>
    ({a: (x.a+y.a), b: (x.b+y.b)}), head);

sum(xs);
//=> {a: 11, b: 5}

然后在 xs 的较大切片上的映射中应用该函数:

xs.map((_, i, arr) => sum(arr.slice(0, i+1)));
//=> [ {a:  1, b: 2}
//=> , {a:  3, b: 6}
//=> , {a: 11, b: 5}]