Artigo original escrito por: Alex Permyakov
Artigo original: How to simplify your codebase with map(), reduce(), and filter() in JavaScript
Traduzido e adaptado por: Daniel Rosa

Ao lermos sobre Array.reduce e ver como ele é legal, o primeiro e, às vezes, único exemplo que encontramos é o da soma de números. Essa não é nossa definição de "útil".

Além disso, eu nunca o vi em uma base de código real. Porém, algo que já vi muito foram instruções com laços for de 7 a 8 linhas para resolver uma tarefa comum em situações que o Array.reduce poderia resolver com apenas uma linha.

Recentemente, eu reescrevi alguns módulos usando essas funções incríveis. Me surpreendeu a simplicidade que veio a se tornar a base de código. Por isso, abaixo você encontrará uma lista de coisas boas.

Se você tiver um bom exemplo do uso dos métodos map ou reduce — eu adoraria saber a respeito.

Vamos começar!

1. Remover duplicatas de um array de números/strings

Bem, esse é o único código que não trata de map/reduce/filter, mas é tão compacto que é difícil não o colocar na lista. Além disso, também vamos usá-lo em alguns exemplos.

const values = [3, 1, 3, 5, 2, 4, 4, 4];
const uniqueValues = [...new Set(values)];

// uniqueValues é [3, 1, 5, 2, 4]

2. Uma busca simples (que diferencie maiúsculas e minúsculas)

O método filter() cria um array com todos os elementos que passam no teste implementado pela função fornecida.

const users = [
  { id: 11, name: 'Adam', age: 23, group: 'editor' },
  { id: 47, name: 'John', age: 28, group: 'admin' },
  { id: 85, name: 'William', age: 34, group: 'editor' },
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
];

let res = users.filter(it => it.name.includes('oli'));

// res é []

3. Uma busca simples (sem diferenciar maiúsculas e minúsculas)

let res = users.filter(it => new RegExp('oli', "i").test(it.name));

// res é
[
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
]

4. Verificar se algum dos usuários tem direitos de administrador

O método some() testa se pelo menos um elemento do array passa no teste implementado pela função fornecida.

const hasAdmin = users.some(user => user.group === 'admin');

// hasAdmin é true

5. "Achatando" um array de arrays

O resultado da primeira iteração é igual a: […[], …[1, 2, 3]], o que significa que ele se transforma em [1, 2, 3] — esse valor, nós fornecemos como um 'acc' (acumulador) na segunda iteração e assim por diante.

const nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let flat = nested.reduce((acc, it) => [...acc, ...it], []);

// flat é [1, 2, 3, 4, 5, 6, 7, 8, 9]

Podemos melhorar um pouco esse código omitindo o array vazio [] como o segundo argumento para reduce(). Então, o primeiro valor de nested (aninhado) será usado como valor inicial de acc. Obrigado, Vladimir Efanov.

let flat = nested.reduce((acc, it) => [...acc, ...it]);

// flat é [1, 2, 3, 4, 5, 6, 7, 8, 9]

Observe que usar o operador de spread dentro de um reduce não ajuda muito o desempenho. Esse exemplo é um caso em que a medição do desempenho faz sentido para seu caso de uso. ☝️

Graças a Paweł Wolak, aqui temos um modo mais curto, sem Array.reduce:

let flat = [].concat.apply([], nested);

Além disso, Array.flat está chegando, mas ainda é um recurso experimental.

6. Criar um objeto que contenha a frequência da chave especificada

Vamos agrupar e contar a propriedade 'age' para cada item do array:

const users = [
  { id: 11, name: 'Adam', age: 23, group: 'editor' },
  { id: 47, name: 'John', age: 28, group: 'admin' },
  { id: 85, name: 'William', age: 34, group: 'editor' },
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
];

const groupByAge = users.reduce((acc, it) => {
  acc[it.age] = acc[it.age] + 1 || 1;
  return acc;
}, {});

// groupByAge é {23: 1, 28: 2, 34: 1}

Obrigado, sai krishna, por sugerir esta!

7. Indexar um array de objetos (tabela de procura)

Em vez de processar todo o array para encontrar um usuário por id, podemos construir um objeto onde o id do usuário representa uma chave (com tempo de procura constante).

const uTable = users.reduce((acc, it) => (acc[it.id] = it, acc), {})

// uTable é igual a:
{
  11: { id: 11, name: 'Adam', age: 23, group: 'editor' },
  47: { id: 47, name: 'John', age: 28, group: 'admin' },
  85: { id: 85, name: 'William', age: 34, group: 'editor' },
  97: { id: 97, name: 'Oliver', age: 28, group: 'admin' }
}

É útil quando você tem de acessar seus dados por id, como em uTable[85].name, com frequência.

8. Extrair os valores exclusivos de determinada chave de cada item no array

Vamos criar uma lista de grupos de usuários existentes. O método map() cria um array com os resultados da chamada de uma função fornecida em cada elemento do array que faz a chamada.

const listOfUserGroups = [...new Set(users.map(it => it.group))];

// listOfUserGroups é ['editor', 'admin'];

9. Inversão do mapa de chave-valor do objeto

const cities = {
  Lyon: 'France',
  Berlin: 'Germany',
  Paris: 'France'
};

let countries = Object.keys(cities).reduce(
  (acc, k) => (acc[cities[k]] = [...(acc[cities[k]] || []), k], acc) , {});
  
// countries é
{
  France: ["Lyon", "Paris"],
  Germany: ["Berlin"]
}

Esse código de uma linha parece bastante traiçoeiro. Usamos o operador de vírgula aqui. Isso significa que retornamos o último valor dentro dos parênteses — acc. Vamos reescrever esse exemplo em um modo mais pronto para produção e com melhor desempenho:

let countries = Object.keys(cities).reduce((acc, k) => {
  let country = cities[k];
  acc[country] = acc[country] || [];
  acc[country].push(k);
  return acc;
}, {});

Aqui, não usamos o operador de spread — ele cria um array em cada chamada de reduce(), o que resulta em um grande ônus em termos de desempenho: O(n²). Em vez disso, usamos o bom e velho método push().

10. Criar um array de valores em Fahrenheit a partir de um array de valores em Celsius

Pense nele como sendo um processamento de cada elemento usando uma fórmula específica.

const celsius = [-15, -5, 0, 10, 16, 20, 24, 32]
const fahrenheit = celsius.map(t => t * 1.8 + 32);

// fahrenheit é [5, 23, 32, 50, 60.8, 68, 75.2, 89.6]

11. Codificar um objeto em uma string de consulta

const params = {lat: 45, lng: 6, alt: 1000};

const queryString = Object.entries(params).map(p => encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1])).join('&')

// queryString é "lat=45&lng=6&alt=1000"

12. Imprimir uma tabela de usuários como strings legíveis e somente de chaves especificadas

Às vezes, você quer imprimir um array de objetos com chaves selecionadas como uma string, mas percebe que JSON.stringify não será a melhor ideia.

const users = [
  { id: 11, name: 'Adam', age: 23, group: 'editor' },
  { id: 47, name: 'John', age: 28, group: 'admin' },
  { id: 85, name: 'William', age: 34, group: 'editor' },
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
];

users.map(({id, age, group}) => `\n${id} ${age} ${group}`).join('')

// ele retornará:
"
11 23 editor
47 28 admin
85 34 editor
97 28 admin"

JSON.stringify pode fazer com que o resultado em forma de string seja mais legível, mas não terá a forma de uma tabela:

JSON.stringify(users, ['id', 'name', 'group'], 2);

// ele retornará:
"[
  {
    "id": 11,
    "name": "Adam",
    "group": "editor"
  },
  {
    "id": 47,
    "name": "John",
    "group": "admin"
  },
  {
    "id": 85,
    "name": "William",
    "group": "editor"
  },
  {
    "id": 97,
    "name": "Oliver",
    "group": "admin"
  }
]"

13. Encontrar e substituir um par chave-valor em um array de objetos

Digamos que nossa intenção é retornar a idade (age) de John. Se você sabe o índice, pode escrever esta linha: users[1].age = 29. No entanto, vejamos outra maneira de fazer isso:

const updatedUsers = users.map(
  p => p.id !== 47 ? p : {...p, age: p.age + 1}
);

// John está fazendo 29 anos

Aqui, em vez de alterar o único item de nosso array, criamos um com apenas um elemento diferente. Agora, podemos comparar os arrays apenas por referência, usando updatedUsers == users, o que é muito rápido! O React.js usa essa abordagem para acelerar o processo de reconciliação. Aqui temos uma explicação (em inglês).

14. União (A ∪ B) de arrays

Vamos codificar a importação e a chamada do método union do lodash.

const arrA = [1, 4, 3, 2];
const arrB = [5, 2, 6, 7, 1];

[...new Set([...arrA, ...arrB])]; // retornará [1, 4, 3, 2, 5, 6, 7]

15. Interseção (A ∩ B) de arrays

Este é o último!

const arrA = [1, 4, 3, 2];
const arrB = [5, 2, 6, 7, 1];

arrA.filter(it => arrB.includes(it)); // retornará [1, 2]

Como exercício, tente implementar a diferença (A \ B) entre os arrays. Dica: use um ponto de exclamação.

Obrigado a Asmor e a incarnatethegreat por seus comentários sobre o nº 9.

É isso!

Se o artigo foi útil a você, comente-o e compartilhe-o para mostrar seu apoio!

Abaixo vemos mais alguns artigos do autor (em inglês):

How to get started with internationalization in JavaScript

Production ready Node.js REST APIs Setup using TypeScript, PostgreSQL and Redis

Obrigado pela leitura ❤️