Muito bom! Complementando, acho que a principal lição é que map
sempre retorna outro array, então se vc não precisa desse outro array, não deveria usar map
.
Tenho visto muitas pessoas usando map
indevidamente como substituto de for
/forEach
, o que apesar de "funcionar", é um uso torto. Por exemplo:
const array = [1,2,3,4];
array.map(n => console.log(n * 2));
O código até "funciona" (imprime os valores desejados), mas é um uso torto por vários motivos:
map
retorna outro array, que no caso não foi usado pra nada. Ou seja, gerou outro array à toa.
- semanticamente fica confuso, pois o uso de
map
sugere que os valores serão mapeados e deseja-se ter/usar o outro array com os resultados. Mas como não foi isso que aconteceu, acaba causando confusão, principalmente se outras pessoas forem dar manutenção no código ("Por que não usou um for
?")
Neste caso seria muito mais adequado um loop:
for (const n of array) {
console.log(n * 2);
}
Ou o forEach
:
array.forEach(n => console.log(n * 2));
Lembrando ainda que forEach
(assim como map
, filter
, reduce
, etc) recebe uma função de callback que é executada para cada elemento. Claro que para poucos arrays pequenos não faz diferença, mas tem que lembrar que chamadas de função têm seu custo, que pode ou não ser desprezível dependendo do caso.
Quanto a um dos seus exemplos, vale lembrar que ao retornar o mesmo objeto, uma alteração neste causa uma mudança em todos os arrays:
const characters = [
{ name: 'Goku', race: 'saiyajin' },
{ name: 'Cell', race: 'android' },
{ name: 'Kuririn', race: 'terráqueo' },
{ name: 'Bulma', race: 'terráqueo' },
{ name: 'Vegeta', race: 'saiyajin' },
{ name: 'Mestre Kame', race: 'terráqueo' },
{ name: 'Kami-Sama', race: 'namekuseijin' }
];
const mapeado = characters.map(character => {
if (character.race === 'terráqueo')
return {
name: character.name.toUpperCase(),
race: character.race
};
return character;
});
console.log(mapeado[0]);
characters[0].name = 'GOKU';
console.log(mapeado[0]);
Como o objeto referente a "Goku" é o mesmo nos dois arrays, qualquer alteração feita em um reflete no outro.
Para evitar isso, teria que retornar uma cópia mesmo, então bastaria mudar para:
const mapeado = characters.map(character => {
if (character.race === 'terráqueo')
return {
name: character.name.toUpperCase(),
race: character.race
};
return { ...character };
});
Lembrando que neste caso estou fazendo uma shallow copy, ou seja, se o objeto character
tiver outros objetos dentro ele, o problema persistiria. Neste caso, teria que ser feita uma deep copy (dê uma olhada aqui para mais detalhes).
Por fim, a função também poderia ser assim:
const mapeado = characters.map(character => {
const novo = { ...character };
if (character.race === 'terráqueo')
novo.name = character.name.toUpperCase();
return novo;
});
Esta abordagem seria mais vantajosa caso cada objeto tivesse muitas propriedades, pois aí vc já copia todas de uma vez. Neste caso não faz tanta diferença porque são só duas (name
e race
), mas se tivesse mais, seria bem massante ficar copiando uma a uma.