Se você está começando em JavaScript, talvez não tenha ouvido falar em .map(), .reduce() e .filter(). Por questões de compatibilidade (Internet Explorer 8 não dava suporte), esses métodos começaram a ser amplamente utilizados há não muito tempo. Mas se você não precisa oferecer compatibilidade com esse navegador muito antigo, deve se familiarizar com eles. PS: Este artigo provavelmente se aplica a qualquer outra linguagem de programação que você esteja usando, pois esses são conceitos não exclusivos de JavaScript.
.map()
Deixe-me explicar como isso funciona com um exemplo simples. Digamos que você tenha recebido uma matriz contendo vários objetos – cada um representando uma pessoa. O que você realmente precisa no final, porém, é um array contendo apenas o id.
1 2 3 4 5 6 7 8 |
//O Que você tem var officers = [ { id: 20, name: 'Captain Piett' }, { id: 24, name: 'General Veers' }, { id: 56, name: 'Admiral Ozzel' }, { id: 88, name: 'Commander Jerjerrod' } ];// O que você precisa [20, 24, 56, 88] |
Existem várias maneiras de fazer isso. Você pode utilizar os métodos de iteração .forEach(), .for (… of) ou um simples .for() para recuperar o valor do atributo. Vamos analisar:
1 2 3 4 |
var officersIds = []; officers.forEach(function (officer) { officersIds.push(officer.id); }); |
Observe que você tem que criar um array vazio de antemão. Utilizando o .map():
1 2 3 |
var officersIds = officers.map(function (officer) { return officer.id }); |
Podemos ser ainda mais concisos com a sintaxe inline (requer suporte ES6, Babel ou TypeScript):
1 |
const officersIds = officers.map(officer => officer.id); |
Então, como o .map() funciona? O callback passado como parâmetro é executado para cada valor no array e retorna cada novo valor no array resultante. Lembre-se de que o array resultante sempre terá o mesmo comprimento do array original.
.reduce()
Assim como o .map(), .reduce() também executa um callback para cada elemento de um array. A diferença aqui é que o .reduce() passa o resultado desse callback (o acumulador) de um elemento para o outro. O acumulador pode ser de praticamente qualquer tipo (inteiro, string, objeto, etc.) e deve ser instanciado ou passado ao chamar .reduce (). Vamos analisar com um exemplo. Digamos que você tenha uma série com esses pilotos e seus respectivos anos de experiência:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var pilots = [ { id: 10, name: "Poe Dameron", years: 14, }, { id: 2, name: "Temmin 'Snap' Wexley", years: 30, }, { id: 41, name: "Tallissan Lintra", years: 16, }, { id: 99, name: "Ello Asty", years: 22, } ]; |
Supondo que precisamos saber o total de anos de experiência de todos eles. Com .reduce() é bem simples:
1 2 3 |
var totalYears = pilots.reduce(function (accumulator, pilot) { return accumulator + pilot.years; }, 0); |
Observe que eu defini o valor inicial como 0. Eu também poderia ter usado uma variável existente, se necessário. Depois de executar o callback para cada elemento do array, o valor final será a redução do nosso acumulador (no nosso caso: 82). Vamos ver como isso pode ser reduzido com a sintaxe inline:
1 |
const totalYears = pilots.reduce((acc, pilot) => acc + pilot.years, 0); |
Agora, digamos que eu queira descobrir qual piloto é o mais experiente. Podemos usar o .reduce() também:
1 2 3 |
var mostExpPilot = pilots.reduce(function (oldest, pilot) { return (oldest.years || 0) > pilot.years ? oldest : pilot; }, {}); |
Neste caso, nomeei meu acumulador como oldest. Meu retorno de chamada compara o acumulador para cada piloto. Se um piloto tem mais anos de experiência do que o armazenado em oldest, então esse piloto se torna o novo mais antigo. Como você pode ver, usar .reduce() é uma maneira fácil de gerar um único valor ou objeto a partir de um array.
.filter()
E se você tiver um array, mas quiser selecionar apenas alguns dos elementos nela baseado em algum critério? É aí que entra .filter()! Aqui estão nossos dados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var pilots = [ { id: 2, name: "Wedge Antilles", faction: "Rebels", }, { id: 8, name: "Ciena Ree", faction: "Empire", }, { id: 40, name: "Iden Versio", faction: "Empire", }, { id: 66, name: "Thane Kyrell", faction: "Rebels", } ]; |
Digamos que queremos dois arrays agora: uma para pilotos rebeldes e outra para pilotos do império. Com .filter() se torna incrívelmente fácil:
1 2 3 4 5 |
var rebels = pilots.filter(function (pilot) { return pilot.faction === "Rebels"; });var empire = pilots.filter(function (pilot) { return pilot.faction === "Empire"; }); |
De forma ainda mais enxuta com a sintaxe inline:
1 2 |
const rebels = pilots.filter(pilot => pilot.faction === "Rebels"); const empire = pilots.filter(pilot => pilot.faction === "Empire"); |
Basicamente, se a função no callback retornar verdadeiro, o elemento atual estará no array resultante.
Combinando .map(), .reduce() e .filter()
Como os três méotods são chamados em array e como .map () e .filter () retornam arrays, podemos encadear facilmente nossas chamadas. Vejamos outro exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
var personnel = [ { id: 5, name: "Luke Skywalker", pilotingScore: 98, shootingScore: 56, isForceUser: true, }, { id: 82, name: "Sabine Wren", pilotingScore: 73, shootingScore: 99, isForceUser: false, }, { id: 22, name: "Zeb Orellios", pilotingScore: 20, shootingScore: 59, isForceUser: false, }, { id: 15, name: "Ezra Bridger", pilotingScore: 43, shootingScore: 67, isForceUser: true, }, { id: 11, name: "Caleb Dume", pilotingScore: 71, shootingScore: 85, isForceUser: true, }, ]; |
Nosso objetivo: obter a pontuação total apenas dos usuários da força. Vamos fazer passo a passo. Primeiro, precisamos filtrar apenas o pessoal que pode usar a força:
1 2 3 |
var jediPersonnel = personnel.filter(function (person) { return person.isForceUser; });// Resultado: [{...}, {...}, {...}] (Luke, Ezra e Caleb) |
Com isso, temos 3 elementos restantes em nosso array resultante. Agora precisamos criar um array contendo a pontuação total de cada Jedi:
1 2 3 |
var jediScores = jediPersonnel.map(function (jedi) { return jedi.pilotingScore + jedi.shootingScore; });// Resultado: [154, 110, 156] |
Utilizando o .reduce() para obter o total:
1 2 3 |
var totalJediScore = jediScores.reduce(function (acc, score) { return acc + score; }, 0);// Resultado: 420 |
E pra ficar divertido… podemos encadear todos esses passos em uma única chamada:
1 2 3 4 5 6 7 8 9 10 |
var totalJediScore = personnel .filter(function (person) { return person.isForceUser; }) .map(function (jedi) { return jedi.pilotingScore + jedi.shootingScore; }) .reduce(function (acc, score) { return acc + score; }, 0); |
Enxugando a sintaxe:
1 2 3 4 |
const totalJediScore = personnel .filter(person => person.isForceUser) .map(jedi => jedi.pilotingScore + jedi.shootingScore) .reduce((acc, score) => acc + score, 0); |
Nota: No meu exemplo anterior, .map () e .filter () nem eram necessários. Poderíamos facilmente obter o mesmo resultado apenas com .reduce (). Mas utilizei todos pelo exemplo.
Para ajudar a associar o propósito, retirado deste post
Se liga aí, porque é a hora da revisão!
Ainda que você possa usar as formas de iteração tradicionais, os métodos apresentados neste post ajudam a obter um resultado através de uma sintaxe menos verbosa e mais concisa. Tente substituir alguns dos seus loops em for com alguns .map(), .reduce() e .filter(), onde forem aplicáveis. O código ganha muito em legibilidade após se familiarizar. Quaisquer dúvidas, utilize a área de comentários ou entre em contato!
Nenhum Comentário