Digamos que quiero sumar a.x
para cada elemento en arr
.
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
Tengo motivos para creer que hacha no está definida en algún momento.
Lo siguiente funciona bien
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
¿Qué estoy haciendo mal en el primer ejemplo?
arr.reduce(function(a,b){return a + b})
al segundo ejemplo.Respuestas:
Después de la primera iteración, está devolviendo un número y luego tratando de obtener la propiedad
x
del mismo para agregarlo al siguiente objeto que esundefined
y las matemáticas que involucranundefined
resultadosNaN
.intente devolver un objeto que contenga una
x
propiedad con la suma de las propiedades x de los parámetros:Explicación agregada de los comentarios:
El valor de retorno de cada iteración de
[].reduce
utilizado comoa
variable en la siguiente iteración.La iteración 1:
a = {x:1}
,b = {x:2}
,{x: 3}
asignado aa
en la iteración 2Iteración 2:
a = {x:3}
,b = {x:4}
.El problema con su ejemplo es que está devolviendo un número literal.
La iteración 1:
a = {x:1}
,b = {x:2}
,// returns 3
comoa
en la siguiente iteraciónIteración 2:
a = 3
,b = {x:2}
vuelveNaN
Un número literal
3
no tiene (normalmente) una propiedad llamada,x
por lo que esundefined
yundefined + b.x
devuelveNaN
yNaN + <anything>
siempre esNaN
Aclaración : prefiero mi método sobre la otra respuesta principal en este hilo, ya que no estoy de acuerdo con la idea de que pasar un parámetro opcional para reducir con un número mágico para obtener un número primitivo es más limpio. Puede dar lugar a menos líneas escritas, pero en mi opinión es menos legible.
fuente
x
clave esperada para que funcione.Una forma más limpia de lograr esto es proporcionando un valor inicial:
La primera vez que se llama a la función anónima, se llama con
(0, {x: 1})
y vuelve0 + 1 = 1
. La próxima vez, se llama con(1, {x: 2})
y regresa1 + 2 = 3
. Luego se llama con(3, {x: 4})
, finalmente regresando7
.fuente
reduce
.TL; DR, establece el valor inicial
Usando la desestructuración
arr.reduce( ( sum, { x } ) => sum + x , 0)
Sin desestructuración
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
Con mecanografiado
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
Probemos el método de desestructuración:
La clave para esto es establecer el valor inicial. El valor de retorno se convierte en el primer parámetro de la siguiente iteración.
La técnica utilizada en la respuesta superior no es idiomática
La respuesta aceptada propone NO pasar el valor "opcional". Esto está mal, ya que la forma idiomática es que siempre se incluya el segundo parámetro . ¿Por qué? Tres razones:
1. Peligroso : no pasar el valor inicial es peligroso y puede crear efectos secundarios y mutaciones si la función de devolución de llamada es descuidada.
Mirad
Sin embargo, si lo hubiéramos hecho de esta manera, con el valor inicial:
Para el registro, a menos que tenga la intención de mutar el objeto original, establezca el primer parámetro
Object.assign
en un objeto vacío. De esta manera:Object.assign({}, a, b, c)
.2 - Mejor inferencia de tipos: al usar una herramienta como TypeScript o un editor como VS Code, obtienes el beneficio de decirle al compilador la inicial y puede detectar errores si lo estás haciendo mal. Si no establece el valor inicial, en muchas situaciones podría no ser capaz de adivinar y podría terminar con errores de tiempo de ejecución espeluznantes.
3 - Respeta a los Functores - JavaScript brilla mejor cuando se desata su hijo funcional interno. En el mundo funcional, hay un estándar sobre cómo "doblar" o
reduce
una matriz. Cuando dobla o aplica un catamorfismo a la matriz, toma los valores de esa matriz para construir un nuevo tipo. Debe comunicar el tipo resultante; debe hacerlo incluso si el tipo final es el de los valores de la matriz, otra matriz o cualquier otro tipo.Pensemos de otra manera. En JavaScript, las funciones se pueden pasar como datos, así es como funcionan las devoluciones de llamada, ¿cuál es el resultado del siguiente código?
[1,2,3].reduce(callback)
¿Devolverá un número? ¿Un objeto? Esto lo hace más claro
[1,2,3].reduce(callback,0)
Lea más sobre las especificaciones de programación funcional aquí: https://github.com/fantasyland/fantasy-land#foldable
Un poco más de fondo
El
reduce
método toma dos parámetros,La
callback
función toma los siguientes parámetrosPara la primera iteración,
Si
initialItem
se proporciona, lareduce
función pasainitialItem
como elaccumulator
y el primer elemento de la matriz comoitemInArray
.Si
initialItem
se no se proporciona, lareduce
función pasa el primer elemento en la matriz como elinitialItem
y el segundo elemento de la matriz comoitemInArray
la que puede ser confuso comportamiento.Enseño y recomiendo siempre establecer el valor inicial de reducir.
Puede consultar la documentación en:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
¡Espero que esto ayude!
fuente
Otros han respondido a esta pregunta, pero pensé que tendría otro enfoque. En lugar de ir directamente a la suma de ax, puede combinar un mapa (de ax a x) y reducir (para agregar las x):
Es cierto que probablemente sea un poco más lento, pero pensé que valía la pena mencionarlo como una opción.
fuente
Para formalizar lo que se ha señalado, un reductor es un catamorfismo que toma dos argumentos que pueden ser del mismo tipo por coincidencia, y devuelve un tipo que coincide con el primer argumento.
Eso significa que el cuerpo del reductor debe estar relacionado con la conversión
currentValue
y el valor actualaccumulator
del valor nuevoaccumulator
.Esto funciona de manera directa, cuando se agrega, porque los valores del acumulador y del elemento son del mismo tipo (pero tienen diferentes propósitos).
Esto simplemente funciona porque son todos números.
Ahora le estamos dando un valor inicial al agregador. El valor inicial debe ser el tipo que espera que sea el agregador (el tipo que espera que salga como valor final), en la gran mayoría de los casos. Si bien no está obligado a hacer esto (y no debería serlo), es importante tenerlo en cuenta.
Una vez que sepa eso, puede escribir reducciones significativas para otros problemas de relación n: 1.
Eliminar palabras repetidas:
Proporcionar un recuento de todas las palabras encontradas:
Reducción de una matriz de matrices, a una única matriz plana:
Cada vez que esté buscando pasar de una variedad de cosas, a un solo valor que no coincida con 1: 1, reducir es algo que podría considerar.
De hecho, el mapa y el filtro pueden implementarse como reducciones:
Espero que esto proporcione algún contexto adicional sobre cómo usarlo
reduce
.La única adición a esto, en la que aún no he entrado, es cuando existe la expectativa de que los tipos de entrada y salida están específicamente destinados a ser dinámicos, porque los elementos de la matriz son funciones:
fuente
Para la primera iteración, 'a' será el primer objeto de la matriz, por lo tanto ax + bx devolverá 1 + 2, es decir 3. Ahora en la siguiente iteración, el 3 devuelto se asigna a a, por lo que a es un número que ahora se llama ax le dará NaN.
La solución simple es primero mapear los números en la matriz y luego reducirlos de la siguiente manera:
aquí
arr.map(a=>a.x)
proporcionará una serie de números [1,2,4] que ahora.reduce(function(a,b){return a+b})
usará simplemente agregará estos números sin ningún problemaOtra solución simple es proporcionar una suma inicial como cero asignando 0 a 'a' como se muestra a continuación:
fuente
En cada paso de su reducción, no está devolviendo un nuevo
{x:???}
objeto. Entonces, o debes hacer:o necesitas hacer
fuente
En el primer paso, funcionará bien ya que el valor de
a
será 1 y el deb
será 2, pero como 2 + 1 se devolverá y en el siguiente paso el valor deb
será el valor de retornofrom step 1 i.e 3
y, porb.x
lo tanto , será indefinido. .y undefined + anyNumber será NaN y es por eso que obtienes ese resultado.En cambio, puede intentar esto dando el valor inicial como cero, es decir
fuente
Si tiene un objeto complejo con muchos datos, como una matriz de objetos, puede tomar un enfoque paso a paso para resolver esto.
Por ejemplo:
Primero, debe asignar su matriz a una nueva matriz de su interés, podría ser una nueva matriz de valores en este ejemplo.
Esta función de devolución de llamada devolverá una nueva matriz que contiene solo valores de la matriz original y la almacenará en valores const. Ahora su const de valores es una matriz como esta:
Y ahora estás listo para realizar tu reducción:
Como puede ver, el método reduce ejecuta la función de devolución de llamada varias veces. Para cada vez, toma el valor actual del artículo en la matriz y suma con el acumulador. Entonces, para sumarlo correctamente, debe establecer el valor inicial de su acumulador como el segundo argumento del método de reducción.
Ahora tienes tu nueva suma constante con el valor de 30.
fuente
la función reduce itera sobre una colección
se traduce en:
Para comprender las entradas y salidas de la función "reducir", le sugiero que mire el código fuente de esta función. La biblioteca Lodash tiene una función de reducción que funciona exactamente igual que la función de "reducción" en ES6.
Aquí está el enlace: reducir el código fuente
fuente
para devolver una suma de todos los
x
accesorios:fuente
Podrías usar el mapa y reducir funciones
fuente
La función de reducción de matriz toma tres parámetros, es decir, initialValue (el valor predeterminado es 0), acumulador y valor actual. Por defecto, el valor de initialValue será "0". que es tomado por acumulador
Veamos esto en código.
Ahora mismo ejemplo con valor inicial:
Lo mismo se aplica también a las matrices de objetos (la pregunta actual de stackoverflow):
UNA COSA PARA DESCUBRIRSE es InitialValue por defecto es 0 y se le puede dar cualquier cosa, quiero decir {}, [] y número
fuente
fuente
no debe usar ax para el acumulador, en su lugar puede hacer esto `arr = [{x: 1}, {x: 2}, {x: 4}]
arr.reduce (función (a, b) {a + bx}, 0) `
fuente