Skip to content
This page has been auto-translated and may contain errors.View in English

Métodos de array

docs.scrimba.com

Você tem uma lista de preços e quer uma segunda lista com imposto adicionado a cada um. Você poderia escrever um loop for que percorre o array, faz a soma e adiciona cada resultado em um novo array. Isso funciona, mas é muito código para uma ideia simples: pegue cada item e mude-o. Métodos de array são as ferramentas built-in exatamente para esse tipo de trabalho. Eles permitem dizer o que você quer fazer com uma lista sem detalhar o loop que faz isso, e este capítulo cobre aqueles que você vai usar diariamente. Eles se baseiam diretamente em arrays e funções, então mantenha ambos à mão.

map: mude cada item

map é um método: uma função que pertence a um array e faz um trabalho nele. map constrói um novo array executando cada item através de uma pequena função que você passa, e coleta os resultados. Você passa essa função para ele, e ele entrega cada item um de cada vez.

Digamos que você tenha alguns preços e queira cada um duplicado:

js
const prices = [2, 4, 6];
const doubled = prices.map((price) => price * 2);
console.log(doubled); // [4, 8, 12]

A parte (price) => price * 2 é uma função arrow: uma função curta que pega um item (price) e retorna o que deveria se tornar (price * 2). map chama uma vez por item e coloca cada resposta no novo array, na mesma ordem.

map é um método de array que transforma uma lista em uma nova lista do mesmo comprimento. Você passa um callback, uma função que map executa uma vez por item, e tudo o que esse callback retorna se torna o item na mesma posição do resultado.

js
const prices = [2, 4, 6];
const doubled = prices.map((price) => price * 2);
console.log(doubled); // [4, 8, 12]

Compare isso com a versão manual, que faz a mesma coisa com mais partes em movimento:

js
const prices = [2, 4, 6];
const doubled = [];
for (let i = 0; i < prices.length; i += 1) {
  doubled.push(prices[i] * 2);
}
console.log(doubled); // [4, 8, 12]

O loop obriga você a gerenciar um índice, um novo array e um push. map declara a intenção, "transforme cada preço em preço vezes dois", e lida com a papelada para você.

map transforma um array em um novo array do mesmo comprimento aplicando um callback (a função que você passa para ele chamar) a cada elemento. O valor de retorno do callback no índice i se torna o elemento no índice i do resultado. Nada é pulado e nada é adicionado, então a forma é preservada e apenas os valores mudam.

js
const prices = [2, 4, 6];
const withTax = prices.map((price) => price * 1.2);
console.log(withTax); // [2.4, 4.8, 7.2]

O callback também recebe o índice e o array inteiro como segundo e terceiro argumentos, então array.map((value, index) => ...) está disponível quando a posição importa. O valor que ele mantém para você é intenção: map diz "mesmo comprimento, cada item transformado" de relance, onde um loop obriga o leitor a reconstruir isso de um índice, um contador e um push.

Junomapmap pega uma lista e lhe dá uma nova do mesmo comprimento, com cada item executado através da sua pequena função. A função arrow diz em que cada item se transforma, e map faz isso com todos eles em ordem. Use-a sempre que se vir escrevendo um loop apenas para construir uma segunda lista.
Junomapmap é "transforme cada item em um novo array do mesmo comprimento". O callback executa uma vez por item e seu valor de retorno cai na mesma posição. Uma vez que você vê isso como uma substituição para o padrão loop-mais-push, metade dos seus loops vira uma única linha legível.
Junomapmap preserva comprimento e posição: o retorno do callback no índice i se torna o índice i do resultado. O callback também obtém o índice e o array, para quando a posição importa. Isso se lê como intenção onde um loop obriga você a remontar essa intenção de um contador e um push.

filter: mantenha os itens que passam

Onde map muda cada item, filter mantém apenas alguns deles. Você passa uma função que responde sim ou não para cada item, e filter constrói um novo array apenas com os itens que receberam um sim.

js
const scores = [40, 75, 90, 55, 88];
const passing = scores.filter((score) => score >= 60);
console.log(passing); // [75, 90, 88]

A função (score) => score >= 60 retorna true ou false para cada pontuação. filter mantém aqueles onde é true e descarta o resto. O novo array pode ser mais curto que o original, e nunca muda a ordem.

filter retorna um novo array contendo apenas os itens cujo callback retorna true. O callback é um predicado: uma função cujo trabalho é responder uma questão sim-ou-não sobre cada item.

js
const scores = [40, 75, 90, 55, 88];
const passing = scores.filter((score) => score >= 60);
console.log(passing); // [75, 90, 88]

O resultado é frequentemente mais curto que a entrada, e mantém a ordem original. Comparado a um loop com um if dentro dele, filter coloca o teste em primeiro plano e descarta a cerimônia em torno dele. Se o callback retorna true para tudo, você obtém uma cópia completa de volta; se retorna false para tudo, você obtém um array vazio.

filter retorna um novo array dos elementos para os quais o predicado (o callback cujo trabalho é retornar uma resposta verdadeira ou falsa) vale. O comprimento não é preservado: o resultado vai de vazio até uma cópia completa, e a ordem é mantida.

js
const scores = [40, 75, 90, 55, 88];
const passing = scores.filter((score) => score >= 60);
console.log(passing); // [75, 90, 88]

Duas coisas valem ser mantidas claras. Primeiro, filter decide se um item sobrevive; ele nunca muda o item, então combine com map quando você precisa tanto selecionar quanto transformar. Segundo, o retorno do callback é coagido a um booleano, então um predicado que acidentalmente retorna 0 ou undefined descarta itens que você pretendia manter. Retorne uma comparação explícita e essa classe de bug desaparece.

Junofilterfilter mantém os itens que passam em um teste e descarta o resto, então o novo array é frequentemente mais curto. Sua função responde sim ou não para cada item, e apenas os sins vêm através. Nunca reordena nada, então o que sobrevive fica na ordem em que começou.
Junofilterfilter mantém itens cujo callback retorna true, lhe dando um novo array que é do mesmo comprimento ou mais curto. O callback é um predicado: um teste sim-ou-não, não um transformador. Tudo true dá uma cópia completa, tudo false dá um array vazio, e a ordem é sempre preservada.
Junofilterfilter seleciona, map transforma, e manter essa separação limpa é por que eles encadeiam tão bem. O retorno do callback é coagido a um booleano, então um predicado que deixa escapar um 0 ou undefined silenciosamente descarta itens que você queria. Retorne uma comparação explícita e essa classe inteira de bug desaparece.

reduce: reduza a um valor único

map e filter ambos lhe devolvem um array. reduce é aquele que colapsa um array em um valor único: um total, um máximo, uma string unida. Ele confunde mais pessoas que os outros, então vale a pena ir devagar.

reduce percorre o array enquanto carrega um resultado em execução junto com ele. A cada item ele pergunta: dado o resultado até agora e este item, qual é o novo resultado?

js
const prices = [10, 20, 30];
const total = prices.reduce((sum, price) => sum + price, 0);
console.log(total); // 60

A função recebe duas coisas: sum, o total em execução até agora, e price, o item atual. Ela retorna sum + price, que se torna o novo total em execução para o próximo item. O 0 no final é onde o total começa. Então vai 0 + 10, depois 10 + 20, depois 30 + 30, chegando a 60.

reduce transforma um array inteiro em um valor executando um acumulador: um resultado em execução que cada passo atualiza e passa adiante. O callback recebe o acumulador e o item atual, e retorna o acumulador para o próximo passo.

js
const prices = [10, 20, 30];
const total = prices.reduce((sum, price) => sum + price, 0);
console.log(total); // 60

Dois argumentos para manter claros: sum é o acumulador (o valor construído até agora), price é o item atual. O segundo argumento para reduce, o 0, é o valor inicial do acumulador. Passo a passo: comece em 0, adicione 10 para obter 10, adicione 20 para obter 30, adicione 30 para obter 60. O nome sum é apenas um rótulo aqui; o acumulador pode ser qualquer tipo, incluindo uma string que você continua adicionando ou um máximo em execução.

reduce dobra um array em um valor único encadeando um acumulador (o resultado em execução) através do array. O callback é (accumulator, currentItem) => nextAccumulator, e seu retorno se torna o acumulador entregue ao próximo passo. O segundo argumento para reduce é o acumulador inicial.

js
const prices = [10, 20, 30];
const total = prices.reduce((sum, price) => sum + price, 0);
console.log(total); // 60

Duas regras práticas evitam a maioria dos bugs de reduce. Sempre passe o valor inicial: sem ele, reduce usa o primeiro elemento como a semente e começa no segundo, o que falha em um array vazio e muda silenciosamente o comportamento quando o tipo do seu acumulador difere do tipo do elemento. E todo caminho através do callback deve retornar o acumulador; esquecer o return em um corpo multi-linha deixa undefined como o acumulador para o próximo passo. reduce é o mais geral desses métodos, map e filter podem ambos ser escritos como um reduce, que é exatamente por que também é o mais fácil de tornar ilegível. Use-o quando você está colapsando para um valor, e use algo mais simples quando você não está.

Junoreducereduce carrega um resultado em execução através da lista e o atualiza a cada item. O callback recebe o resultado até agora mais o item atual, e retorna o novo resultado. O último argumento é onde você começa, geralmente 0 para um total, e este clicou para mim no momento em que o li em voz alta passo a passo.
Junoreducereduce reduz um array a um valor usando um acumulador: um resultado em execução que cada passo atualiza e passa adiante. O callback é (accumulator, item) e o último argumento semeia o acumulador. Não precisa ser um número; o acumulador pode ser uma string que você constrói ou um máximo em execução.
Junoreduce Sempre passe o valor inicial, ou reduce semeia do primeiro elemento e falha em um array vazio. Todo caminho através do callback deve retornar o acumulador, ou o próximo passo recebe undefined. É o mais geral desses métodos e o mais fácil de tornar ilegível, então use-o para reduzir a um valor e use algo mais simples quando você não está.

find, some, e every

Esses três são parentes próximos de filter: eles executam um teste sobre o array, mas em vez de um novo array eles respondem uma questão mais estreita.

find lhe dá o primeiro item que passa no teste, não uma lista de todos eles. some e every respondem questões verdadeiro-ou-falso sobre o array inteiro.

js
const scores = [40, 75, 90, 55];

const firstPass = scores.find((score) => score >= 60);
console.log(firstPass); // 75

const anyPass = scores.some((score) => score >= 60);
console.log(anyPass); // true

const allPass = scores.every((score) => score >= 60);
console.log(allPass); // false

find retorna o primeiro item correspondente (75). some pergunta "algum item passou?" e retorna true porque pelo menos um passou. every pergunta "todos passaram?" e retorna false porque 40 e 55 não passaram.

Mesmo estilo de predicado que filter, três respostas diferentes. find retorna o primeiro item correspondente (ou undefined se nenhum corresponder). some retorna true se pelo menos um item passar. every retorna true apenas se todos passarem.

js
const scores = [40, 75, 90, 55];

console.log(scores.find((score) => score >= 60)); // 75
console.log(scores.some((score) => score >= 60)); // true
console.log(scores.every((score) => score >= 60)); // false

Use find quando você quiser um item em vez de uma lista filtrada, e some ou every quando você só precisa de uma resposta sim-ou-não sobre a coleção. Usar filter(...).length > 0 onde some funcionaria funciona, mas verifica cada item e constrói um array descartável para responder uma questão que some responde diretamente.

Três consultas orientadas por predicado que param cedo em vez de construir um novo array. find retorna o primeiro elemento correspondente ou undefined. some retorna true no primeiro elemento que passa. every retorna false no primeiro que falha.

js
const scores = [40, 75, 90, 55];

console.log(scores.find((score) => score >= 60)); // 75
console.log(scores.some((score) => score >= 60)); // true
console.log(scores.every((score) => score >= 60)); // false

O short-circuiting é o ponto: some e every param no momento em que a resposta é decidida, então são mais baratos que um filter sobre o array inteiro quando você só precisa de um booleano. Dois casos extremos para saber: find retornando undefined é indistinguível de encontrar um undefined real, então teste o predicado que você se importa em vez do valor de retorno quando isso for possível. E em um array vazio some é sempre false e every é sempre true (vaziamente, já que não há item para falhar), que é correto mas surpreende pessoas na primeira vez.

Junofind, some, and everyfind lhe dá o primeiro item que passa, não uma lista inteira. some pergunta "algum passou?" e every pergunta "todos passaram?", e ambos respondem com true ou false. Use estes quando você quiser um item ou uma resposta sim-ou-não, não um array filtrado.
Junofind, some, and every Mesmo predicado que filter, três respostas mais estreitas: find dá a primeira correspondência ou undefined, some é true se algum passar, every é true apenas se todos passarem. Use estes em vez de filter(...).length quando você quiser um item ou um booleano, já que declaram a intenção e pulam construir um array descartável.
Junofind, some, and everysome e every executam short-circuit, parando no momento em que a resposta é decidida, então batem filtrar o array inteiro para um booleano. Observe duas arestas: find retornando undefined parece igual a encontrar um undefined real, e em um array vazio some é sempre falso enquanto every é sempre verdadeiro. Ambos estão corretos, ambos surpreendem pessoas uma vez.

Encadeamento e mantendo legibilidade

Os métodos acima compartilham um design que compensa uma vez que você os combina: cada um retorna um novo valor e deixa o array original intocado.

map, filter e o resto nunca mudam o array com o qual você começou. Eles lhe dão um novo array (ou um valor) e deixam o original sozinho.

js
const prices = [10, 20, 30];
const doubled = prices.map((price) => price * 2);
console.log(doubled); // [20, 40, 60]
console.log(prices); // [10, 20, 30]

Porque map e filter ambos devolvem um array, você pode alinhá-los: filtre primeiro, depois mapeie os sobreviventes.

js
const prices = [10, 20, 30, 40];
const bigDoubled = prices.filter((price) => price >= 20).map((price) => price * 2);
console.log(bigDoubled); // [40, 60, 80]

Esses métodos retornam novos arrays e valores sem mudar o original, uma propriedade chamada imutabilidade: seus dados de origem permanecem como eram. É isso que torna o encadeamento seguro. Porque filter retorna um array, você pode chamar map direto em seu resultado:

js
const prices = [10, 20, 30, 40];
const bigDoubled = prices.filter((price) => price >= 20).map((price) => price * 2);
console.log(bigDoubled); // [40, 60, 80]

Leia uma cadeia de cima para baixo como um pipeline: mantenha preços de 20 ou mais, depois duplique cada sobrevivente. A ordem importa, já que filtrar primeiro significa que map é executado sobre menos itens. Encadeamento lê bem até dois ou três passos; além disso, uma variável intermediária nomeada ou um loop simples é frequentemente mais claro que uma cadeia longa.

Esses métodos não são mutáveis: eles retornam novos arrays e valores, deixando a fonte intocada. Essa imutabilidade é o que torna uma cadeia filter(...).map(...) segura de ler como um pipeline, já que nenhum passo volta e edita um array compartilhado por você.

js
const prices = [10, 20, 30, 40];
const bigDoubled = prices.filter((price) => price >= 20).map((price) => price * 2);
console.log(bigDoubled); // [40, 60, 80]

Dois julgamentos decidem se uma cadeia ajuda ou prejudica. Primeiro, custo: cada passo percorre o array e aloca um novo, então uma cadeia de três passos faz três passagens e dois arrays intermediários. Nos tamanhos que você encontra dia a dia isso é irrelevante e as vitórias de legibilidade; em um caminho quente sobre um array grande pode importar, e um único loop ou reduce que faz o trabalho em uma passagem é a resposta. Segundo, clareza: uma cadeia ganha seu lugar quando cada passo é uma ideia clara. No momento em que um callback cresce um corpo com ramificações, ou a cadeia passa por três passos, um loop simples for com uma variável bem nomeada geralmente lê melhor. Prefira a versão que um colega entende mais rápido, não aquela que cabe em uma linha.

JunoChaining and staying readable Esses métodos nunca mudam o array com o qual você começa, eles lhe dão um novo, então o original é sempre seguro. Porque filter e map ambos retornam arrays, você pode encadeá-los: filtre primeiro, depois mapeie o que sobrou. Leia a cadeia de cima para baixo, um passo de cada vez.
JunoChaining and staying readable Imutabilidade, deixando o array original sozinho, é o que torna o encadeamento seguro. Leia filter(...).map(...) como um pipeline de cima para baixo, e lembre-se que a ordem importa já que filtrar primeiro significa mapear menos itens. Passado dois ou três passos, uma variável nomeada ou um loop simples geralmente lê mais claro que uma longa cadeia.
JunoChaining and staying readable Cada passo é uma passagem e uma alocação, então uma cadeia é boa em tamanhos cotidianos e vale colapsá-la em um loop ou um reduce em um caminho quente sobre dados grandes. Clareza é o outro julgamento: uma cadeia ganha seu lugar quando cada passo é uma ideia clara, e um loop simples vence uma vez que um callback cresce ramificações. Escreva a versão que um colega lê mais rápido, não a mais curta.

Para onde ir daqui

map, filter e reduce cobrem a maioria do que você fará com uma lista: transforme-a, estreite-a ou reduza-a. find, some e every respondem as questões mais estreitas. Uma vez que estes se sintam naturais, a maioria dos seus loops for sobre um array vira uma linha ou duas que diz o que você pretendia.

O próximo passo natural é objetos, onde arrays começam mantendo itens mais ricos que números e strings simples, e esses mesmos métodos continuam funcionando neles. Se qualquer da sintaxe de callback aqui se sentiu instável, uma passagem de volta através de funções faz o resto clicar em lugar.