Digamos que eu queira somar a.x
para cada elemento em arr
.
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
Eu tenho motivos para acreditar que ax está indefinido em algum momento.
O seguinte funciona bem
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
O que estou fazendo de errado no primeiro exemplo?
arr.reduce(function(a,b){return a + b})
no segundo exemplo.Respostas:
Após a primeira iteração, você está retornando um número e tentando obter propriedade
x
dele para adicionar ao próximo objeto, que é aundefined
matemática que envolveundefined
resultadosNaN
.tente retornar um objeto que contenha uma
x
propriedade com a soma das propriedades x dos parâmetros:Explicação adicionada a partir dos comentários:
O valor de retorno de cada iteração
[].reduce
usada como aa
variável na próxima iteração.Iteração 1:
a = {x:1}
,b = {x:2}
,{x: 3}
atribuído aa
na iteração 2Iteração 2:
a = {x:3}
,b = {x:4}
.O problema com o seu exemplo é que você está retornando um número literal.
Iteração 1:
a = {x:1}
,b = {x:2}
,// returns 3
comoa
na próxima iteraçãoIteração 2:
a = 3
,b = {x:2}
retornosNaN
Um literal numérico
3
não tem (normalmente) uma propriedade chamada,x
portanto éundefined
eundefined + b.x
retornaNaN
eNaN + <anything>
é sempreNaN
Esclarecimento : Prefiro meu método à outra resposta superior neste tópico, pois discordo da idéia de que passar um parâmetro opcional para reduzir com um número mágico e obter um número primitivo é mais limpo. Isso pode resultar em menos linhas escritas, mas é menos legível.
fonte
Uma maneira mais limpa de fazer isso é fornecendo um valor inicial:
A primeira vez que a função anônima é chamada, ela é chamada com
(0, {x: 1})
e retorna0 + 1 = 1
. Na próxima vez, é chamado com(1, {x: 2})
e retorna1 + 2 = 3
. É então chamado com(3, {x: 4})
, finalmente retornando7
.fonte
reduce
.TL; DR, defina o valor inicial
Usando desestruturação
arr.reduce( ( sum, { x } ) => sum + x , 0)
Sem desestruturação
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
Com texto datilografado
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
Vamos tentar o método de desestruturação:
A chave para isso é definir o valor inicial. O valor de retorno se torna o primeiro parâmetro da próxima iteração.
A técnica usada na resposta principal não é idiomática
A resposta aceita propõe NÃO passar o valor "opcional". Isso está errado, pois a maneira idiomática é que o segundo parâmetro seja sempre incluído. Por quê? Três razões:
1. Perigoso - Não passar o valor inicial é perigoso e pode criar efeitos colaterais e mutações se a função de retorno de chamada for descuidada.
Ver
Se, no entanto, tivéssemos feito dessa maneira, com o valor inicial:
Para o registro, a menos que você pretenda alterar o objeto original, defina o primeiro parâmetro de
Object.assign
como um objeto vazio. Como esta:Object.assign({}, a, b, c)
.2 - Melhor Inferência de Tipo usar uma ferramenta como Typescript ou um editor como o VS Code, você obtém o benefício de informar ao compilador a inicial e ele pode detectar erros se você estiver fazendo errado. Se você não definir o valor inicial, em muitas situações, talvez não seja possível adivinhar e você poderá acabar com erros de tempo de execução assustadores.
3 - Respeite os Functors - JavaScript brilha melhor quando seu filho funcional interno é liberado. No mundo funcional, há um padrão sobre como você "dobra" ou
reduce
uma matriz. Quando você dobra ou aplica um catamorfismo à matriz, utiliza os valores dessa matriz para construir um novo tipo. Você precisa comunicar o tipo resultante - você deve fazer isso mesmo que o tipo final seja o dos valores na matriz, outra matriz ou qualquer outro tipo.Vamos pensar de outra maneira. No JavaScript, as funções podem ser transmitidas como dados; é assim que os retornos de chamada funcionam; qual é o resultado do código a seguir?
[1,2,3].reduce(callback)
Ele retornará um número? Um objeto? Isso torna mais claro
[1,2,3].reduce(callback,0)
Leia mais sobre a especificação de programação funcional aqui: https://github.com/fantasyland/fantasy-land#foldable
Um pouco mais de experiência
O
reduce
método usa dois parâmetros,A
callback
função aceita os seguintes parâmetrosPara a primeira iteração,
Se
initialItem
for fornecida, areduce
função passa oinitialItem
como oaccumulator
e o primeiro item da matriz como oitemInArray
.Se não
initialItem
for fornecida, a função passa o primeiro item da matriz como o e o segundo item da matriz, o que pode ser um comportamento confuso.reduce
initialItem
itemInArray
Eu ensino e recomendo sempre definir o valor inicial de reduzir.
Você pode verificar a documentação em:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Espero que isto ajude!
fonte
Outros responderam a essa pergunta, mas pensei em lançar outra abordagem. Em vez de ir diretamente para a soma do machado, você pode combinar um mapa (de machado para x) e reduzir (para adicionar x):
É certo que provavelmente será um pouco mais lento, mas achei que vale a pena mencionar como uma opção.
fonte
Para formalizar o que foi apontado, um redutor é um catamorfismo que pega dois argumentos que podem ser do mesmo tipo por coincidência e retorna um tipo que corresponde ao primeiro argumento.
Isso significa que o corpo do redutor precisa ser sobre a conversão
currentValue
e o valor atual doaccumulator
para o valor do novoaccumulator
.Isso funciona de maneira direta ao adicionar, porque os valores do acumulador e do elemento são do mesmo tipo (mas servem a propósitos diferentes).
Isso só funciona porque são todos números.
Agora estamos dando um valor inicial ao agregador. O valor inicial deve ser o tipo que você espera que o agregador (o tipo que você espera que seja o valor final), na grande maioria dos casos. Enquanto você não é forçado a fazer isso (e não deveria ser), é importante ter em mente.
Depois que você souber disso, poderá escrever reduções significativas para outros problemas de relacionamento n: 1.
Removendo palavras repetidas:
Fornecendo uma contagem de todas as palavras encontradas:
Reduzindo uma matriz de matrizes para uma única matriz plana:
Sempre que você deseja passar de uma variedade de coisas para um único valor que não corresponde a 1: 1, reduzir é algo que você pode considerar.
De fato, o mapa e o filtro podem ser implementados como reduções:
Espero que isso forneça algum contexto adicional de como usar
reduce
.A única adição a isso, que ainda não expliquei, é quando existe uma expectativa de que os tipos de entrada e saída sejam especificamente dinâmicos, porque os elementos da matriz são funções:
fonte
Para a primeira iteração 'a' será o primeiro objeto na matriz, portanto ax + bx retornará 1 + 2 ou seja, 3. Agora, na próxima iteração, o 3 retornado é atribuído a a, então a é um número que está chamando ax vai dar NaN.
A solução simples é primeiro mapear os números na matriz e depois reduzi-los como abaixo:
aqui
arr.map(a=>a.x)
irá fornecer uma matriz de números [1,2,4] agora usando.reduce(function(a,b){return a+b})
irá simplesmente adicionar esses números sem nenhum hasselOutra solução simples é apenas fornecer uma soma inicial como zero atribuindo 0 a 'a' como abaixo:
fonte
Em cada etapa da sua redução, você não está retornando um novo
{x:???}
objeto. Então você precisa:ou você precisa fazer
fonte
Na primeira etapa, funcionará bem, pois o valor de
a
será 1 e o deb
será 2, mas como 2 + 1 será retornado e na próxima etapa o valor deb
será o valor de retornofrom step 1 i.e 3
e, portantob.x
, será indefinido. .e undefined + anyNumber será NaN e é por isso que você está obtendo esse resultado.Em vez disso, você pode tentar isso dando o valor inicial como zero, ou seja,
fonte
Se você possui um objeto complexo com muitos dados, como uma matriz de objetos, pode seguir uma abordagem passo a passo para resolver isso.
Por exemplo:
Primeiro, você deve mapear sua matriz para uma nova matriz de seu interesse; pode ser uma nova matriz de valores neste exemplo.
Essa função de retorno de chamada retornará uma nova matriz contendo apenas valores da matriz original e a armazenará nos valores const. Agora seus valores const são uma matriz como esta:
E agora você está pronto para executar sua redução:
Como você pode ver, o método reduzir executa a função de retorno de chamada várias vezes. Para cada vez, ele pega o valor atual do item na matriz e soma com o acumulador. Portanto, para somar corretamente, você precisa definir o valor inicial do seu acumulador como o segundo argumento do método de redução.
Agora você tem sua nova const sum com o valor de 30.
fonte
a função reduzir itera sobre uma coleção
traduz para:
Para entender as entradas e saídas da função "reduzir", sugiro que você analise o código-fonte dessa função. A biblioteca Lodash possui a função reduzir, que funciona exatamente da mesma forma que a função "reduzir" no ES6.
Aqui está o link: reduza o código fonte
fonte
para retornar uma soma de todos os
x
adereços:fonte
Você pode usar o mapa e reduzir funções
fonte
A função de redução de matriz leva três parâmetros, ou seja, valor inicial (o padrão é 0), acumulador e valor atual. Por padrão, o valor de initialValue será "0". que é tomado pelo acumulador
Vamos ver isso no código.
Agora mesmo exemplo com o valor inicial:
O mesmo se aplica às matrizes de objetos (a questão atual do stackoverflow):
Uma coisa a se desdobrar é InitialValue, por padrão, é 0 e pode receber qualquer coisa que eu queira dizer {}, [] e number
fonte
fonte
você não deve usar axe como acumulador. Em vez disso, você pode fazer assim `arr = [{x: 1}, {x: 2}, {x: 4}]
arr.reduce (função (a, b) {a + bx}, 0) `
fonte