ordenar objetos objects array javascript functional-programming node.js reduce

objects - Javascript reduce en una matriz de objetos



ordenar array de objetos javascript (8)

Después de la primera iteración, está devolviendo un número y luego tratando de obtener la propiedad x de la misma para agregar al siguiente objeto que undefined está undefined y las matemáticas que implican resultados undefined en NaN .

intente devolver un objeto que contenga una propiedad x con la suma de las propiedades x de los parámetros:

var arr = [{x:1},{x:2},{x:4}]; arr.reduce(function (a, b) { return {x: a.x + b.x}; // returns object with property x }) // ES6 arr.reduce((a, b) => ({x: a.x + b.x})); // -> {x: 7}

Explicación agregada de los comentarios:

El valor de retorno de cada iteración de [].reduce utilizado como a variable en la siguiente iteración.

Iteración 1: a = {x:1} , b = {x:2} , {x: 3} asignado a a en la iteración 2

Iteración 2: a = {x:3} , b = {x:4} .

El problema con tu ejemplo es que estás devolviendo un número literal.

function (a, b) { return a.x + b.x; // returns number literal }

Iteración 1: a = {x:1} , b = {x:2} , // returns 3 como a en la siguiente iteración

Iteración 2: a = 3 , b = {x:2} devuelve NaN

Un número literal 3 no tiene (típicamente) una propiedad llamada x por lo que no está undefined y undefined + bx devuelve NaN y NaN + <anything> es siempre NaN

Aclaración : prefiero mi método sobre la otra respuesta superior en este hilo ya que estoy en desacuerdo 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 claro. Puede dar como resultado menos líneas escritas, pero es menos legible.

Digamos que quiero sumar un ax 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 ax no está definido en algún momento.

El siguiente funciona bien

arr = [1,2,4] arr.reduce(function(a,b){return a + b}) >> 7

¿Qué estoy haciendo mal en el primer ejemplo?


En cada paso de su reducción, no está devolviendo un nuevo objeto {x:???} . Entonces debes hacer:

arr = [{x:1},{x:2},{x:4}] arr.reduce(function(a,b){return a + b.x})

o tienes que hacer

arr = [{x:1},{x:2},{x:4}] arr.reduce(function(a,b){return {x: a.x + b.x}; })


En el primer paso, funcionará bien ya que el valor de a será 1 y el de b será 2 pero se devolverá 2 + 1 y en el siguiente paso el valor de b será el valor de retorno from step 1 ie 3 y así bx no estará definido ... e indefinido + anyNumber será NaN y es por eso que está obteniendo ese resultado.

En su lugar, puede probar esto dando un valor inicial como cero, es decir,

arr.reduce(function(a,b){return a + b.x},0);


Otros han respondido a esta pregunta, pero pensé que podría lanzar otro enfoque. En lugar de ir directamente a la suma del hacha, puede combinar un mapa (de ax a x) y reducir (para agregar las x):

arr = [{x:1},{x:2},{x:4}] arr.map(function(a) {return a.x;}) .reduce(function(a,b) {return a + b;});

Es cierto que es probable que sea un poco más lento, pero pensé que valía la pena mencionarlo como una opción.


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.

function reducer (accumulator: X, currentValue: Y): X { }

Eso significa que el cuerpo del reductor necesita convertir el valor actual y el valor actual del accumulator en el valor del nuevo accumulator .

Esto funciona de manera directa, cuando se agrega, porque el acumulador y los valores del elemento son del mismo tipo (pero tienen diferentes propósitos).

[1, 2, 3].reduce((x, y) => x + y);

Esto solo funciona porque todos son números.

[{ age: 5 }, { age: 2 }, { age: 8 }] .reduce((total, thing) => total + thing.age, 0);

Ahora le estamos dando un valor inicial al agregador. El valor inicial debe ser del tipo que espera que sea el agregador (el tipo que espera obtener como el valor final), en la gran mayoría de los casos. Si bien no estás obligado a hacer esto (y no deberías hacerlo), es importante tenerlo en cuenta.

Una vez que lo sepa, puede escribir reducciones significativas para otros problemas de relación n: 1.

Eliminar palabras repetidas:

const skipIfAlreadyFound = (words, word) => words.includes(word) ? words : words.concat(word); const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);

Proporcionando un recuento de todas las palabras encontradas:

const incrementWordCount = (counts, word) => { counts[word] = (counts[word] || 0) + 1; return counts; }; const wordCounts = words.reduce(incrementWordCount, { });

Reducir una matriz de matrices a una única matriz plana:

const concat = (a, b) => a.concat(b); const numbers = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ].reduce(concat, []);

Cada vez que esté buscando pasar de una variedad de cosas a un único valor que no concuerde con un 1: 1, reducir es algo que podría considerar.

De hecho, el mapa y el filtro se pueden implementar como reducciones:

const map = (transform, array) => array.reduce((list, el) => list.concat(transform(el)), []); const filter = (predicate, array) => array.reduce( (list, el) => predicate(el) ? list.concat(el) : list, [] );

Espero que esto proporcione un contexto adicional sobre cómo usar reduce .

La única adición a esto, en la que aún no me he involucrado, 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:

const compose = (...fns) => x => fns.reduceRight((x, f) => f(x), x); const hgfx = h(g(f(x))); const hgf = compose(h, g, f); const hgfy = hgf(y); const hgfz = hgf(z);


Una forma más limpia de lograr esto es proporcionar un valor inicial:

var arr = [{x:1}, {x:2}, {x:4}]; arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7

La primera vez que se llama a la función anónima, se llama con (0, {x: 1}) y devuelve 0 + 1 = 1 . La próxima vez, se llama con (1, {x: 2}) y devuelve 1 + 2 = 3 . Luego se llama con (3, {x: 4}) y finalmente devuelve 7 .


función de reducción itera sobre una colección

arr = [{x:1},{x:2},{x:4}] // is a collection arr.reduce(function(a,b){return a.x + b.x})

se traduce a:

arr.reduce( //for each index in the collection, this callback function is called function ( a, //a = accumulator ,during each callback , value of accumulator is passed inside the variable "a" b, //currentValue , for ex currentValue is {x:1} in 1st callback currentIndex, array ) { return a.x + b.x; }, accumulator // this is returned at the end of arr.reduce call //accumulator = returned value i.e return a.x + b.x in each callback. );

durante cada devolución de llamada de índice, el valor de la variable "acumulador" se pasa al parámetro "a" en la función de devolución de llamada. Si no inicializamos "acumulador", su valor no estará definido. Llamar undefined.x te daría un error.

Para resolver esto, inicialice "acumulador" con valor 0 como la respuesta de Casey mostrada arriba.

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 "reducir" en ES6.

Aquí está el enlace: reducir el código fuente


TL; DR, establecer el valor inicial

arr.reduce( (sum,cur) => sum+cur.x , 0)

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.

Primer pase, sum es cero. cur es el primer valor en la matriz, un objeto. Entonces accedemos a la propiedad x . Agregamos ese número a la sum y lo devolvemos.

Siguiente iteración, lo mismo. sum es ahora el último valor devuelto y agregamos el siguiente artículo, etc.

Si no configuramos ese 0 al final, la primera sum iteración sería un objeto y escribiríamos lógica para manejar un objeto. También tendríamos que devolver un objeto porque la siguiente iteración está configurada para manejar un objeto.

Un poco más de fondo

El método de reduce toma dos parámetros,

Array.prototype.reduce( callback, initialItem )

La función de callback toma los siguientes parámetros

(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }

Cuando se proporciona initialItem , se transfiere como accumulator a la función de callback el primer paso y itemInArray es el primer elemento de la matriz.

Si initialItem no está establecido, reduce toma el primer elemento de la matriz como initialItem y pasa el segundo elemento como itemInArray . Esto puede ser un comportamiento confuso.

Enseño y recomiendo siempre establecer el valor inicial de reducir. Además, a medida que crece como desarrollador y comienza a buscar transductores u otras bibliotecas mejoradas, descubrirá que establecer el valor inicial es imprescindible.

Para obtener un artículo completo sobre Array.prototype.reduce consulte:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

¡Espero que esto ayude!