JavaScript reduce el error de lanzamiento para más de dos elementos

Sep 22 2020

Tengo esta matriz:

const arr = [
  { someProp: [{ amount: 10 }]},
  { someProp: [{ amount: 12 }]},
];

y luego esto reduce fn:

const sum = arr.reduce((prev, curr) => prev.someProp[0].amount + curr.someProp[0].amount);

Funciona si hay dos elementos en la matriz pero con tres arroja:

No se puede leer la propiedad '0' de indefinido "

Respuestas

2 Ifaruki Sep 22 2020 at 13:06

En realidad, solo necesita devolver el acumulador + el nuevo valor.

Quieres comenzar con, 0supongo, por lo que debes agregar 0como segundo parámetro

const arr =
    [
      { someProp: [{ amount: 10 }]},
      { someProp: [{ amount: 12 }]},
      { someProp: [{ amount: 12 }]},
    ];
    
const sum = arr.reduce((acc, item) => item.someProp[0].amount + acc, 0);

console.log(sum);

Si lo quieres más picante aquí con desestructuración

const arr =
    [
      { someProp: [{ amount: 10 }]},
      { someProp: [{ amount: 12 }]},
      { someProp: [{ amount: 12 }]},
    ];
    
const sum = arr.reduce((acc, { someProp: [{ amount }] }) => amount + acc, 0);

console.log(sum);

2 CristianTraìna Sep 22 2020 at 13:06

Siguiendo la respuesta de @Pointy, así es como puede hacer que funcione con cualquier cantidad de elementos:

const sum = arr.reduce((prev, curr) => prev + curr.someProp[0].amount, 0);

preves siempre el elemento devuelto anteriormente, por lo que puede usarlo de manera segura como un número (y dado que solo lo está sumando, puede usarlo 0 como predeterminado, por lo que la primera vez que reducese invoca el valor de prevserá 0)

2 Pointy Sep 22 2020 at 13:00

Devuelve un número. El valor de preves siempre el valor de retorno anterior de la devolución de llamada, no el elemento anterior. Por lo tanto, en la tercera iteración, intenta usar ese número como si fuera una referencia de objeto.

Agregue un segundo parámetro (0) a la .reduce()llamada y cambie la función para tratarla prevcomo el total acumulado (un número simple):

const sum = arr.reduce((prev, curr) => prev + curr.someProp[0].amount, 0);

Funciona como está cuando hay dos elementos porque sin ese segundo parámetro, la primera iteración verá el elemento 0 como prevy el elemento 1 como curr. Ese comportamiento funciona bien cuando tiene una matriz de números y solo desea realizar un cálculo entre ellos, pero en este caso necesita ese valor inicial explícitamente allí como segundo argumento.

PeterDarmis Sep 22 2020 at 13:39

Esta es una solución alternativa ... Usando Array.filter().

const arr = [{
    someProp: [{
      amount: 10
    }]
  },
  {
    someProp: [{
      amount: 12
    }]
  },
  {
    someProp: [{
      amount: -231
    }]
  },
  {
    someProp: [{
      amount: 21265
    }]
  },
  {
    someProp: [{
      amount: 1239
    }]
  },
  {
    someProp: [{
      amount: -6
    }]
  },
];
const sum = arr.filter((val, index) => {
  if (index > 0) {
    arr[index].someProp[0].amount = parseInt(val.someProp[0].amount) + parseInt(arr[index - 1].someProp[0].amount);
  }
  return index == arr.length - 1;
})[0].someProp[0].amount;

console.log(sum);