Separando um array de strings em conjuntos de arrays ordenados alfabeticamente com JavaScript
Neste artigo vamos ver como podemos ordenar um array em JavaScript e separar os resultados em arrays ordenados alfabeticamente. Nosso objetivo será quebrar um array e criar um array para cada letra, de acordo com o conteúdo do array original.
Nos exemplos que vamos ver neste artigo vamos trabalhar com o seguinte array:
var times = ["Palmeiras", "Flamengo", "Cruzeiro", "Internacional", "Fluminense", "Corinthians", "Athletico-PR","Atlético", "Fortaleza","São Paulo","América", "Botafogo", "Santos", "Goiás", "Red Bull Bragantino", "Coritiba", "Cuiabá", "Grêmio", "Vasco", "Bahia"];
O que vamos aprender neste artigo?
* Separando um array de strings utilizando Reduce
* Separando um array de strings utilizando Reduce e convertendo cada elemento num objeto
* Separando um array de strings utilizando Map
Separando um array de strings utilizando Reduce
O método reduce é uma função iteradora que passa por todos os elementos de um array e executa uma regra para cada um desses elementos. Reduce pode receber até quatro parâmetros, que são os seguintes:
1 - acumulador (acc) – valor inicial, ou o valor do callback anterior.
2 - valorAtual (cur) – o valor do elemento atual.
3 - index (idx) – o índice atual.
4 - array original (src)- o array onde a iteração está ocorrendo.
Vamos ver o código em ação e na sequência vamos explicar o seu funcionamento:
var times = ["Palmeiras", "Flamengo", "Cruzeiro", "Internacional", "Fluminense", "Corinthians", "Athletico-PR","Atlético", "Fortaleza","São Paulo","América", "Botafogo", "Santos", "Goiás", "Red Bull Bragantino", "Coritiba", "Cuiabá", "Grêmio", "Vasco", "Bahia"];
var resultados = times.reduce((acc, cur) => {
// cur[0] é a primeira letra de cada item do array
let k = cur[0].toLocaleUpperCase()
// Adiciona numa entrada existente ou cria uma nova
if (acc[k]) acc[k].push(cur)
else acc[k] = [cur]
return acc
}, {})
console.log(resultados);
Se você deseja ver apenas os times que começam com a letra "A", pode fazer da seguinte forma:
console.log(resultados["A"]);
E se dentro da lista de times com a letra "A" você quiser acessar o seguinte elemento basta fazer isso:
console.log(resultados["A"][1]);
Vamos entender como funcionou o reduce. Perceba que ele recebeu apenas dois parâmetros.
var resultados = times.reduce((acc, cur) => {
// Codigo a ser executado
}, {})
Como vimos na explicação sobre o reduce, o primeiro parâmetro, que no nosso exemplo recebeu o nome de "acc" é o acumulador, que pode ser o valor inicial ou o valor do callback anterior. Já o segundo parâmetro, que noneamos como "cur", é o item atual que está sendo iterado.
var resultados = times.reduce((acc, cur) => {
// cur[0] é a primeira letra de cada item do array
let k = cur[0].toLocaleUpperCase()
// ... código a ser executado.
}, {})
Na sequência criamos uma variável chamada "k", com a primeira letra do item que está sendo iterado, para extrair a primeira letra fizemos cur[0]. Convertemos está letra em maísculo utilizando o metodo .toLocaleUpperCase(), mas no nosso caso isso não era necessário já que a primeira letra de cada item de nosso array já está em maiúsculo.
Para terminar, verificamos se no nosso parâmetro acumulador já existe uma entrada de array cuja chave seja a letra atual. Se existir, adiciona um novo elmento utilizando o método push, caso contrário cria um novo elemento no array. Como você deve ter percebido ao testar o código, para cada letra foi criado um array. Perceba que o resultado do nosso reduce foi o primeiro parâmetro, ou seja, o parâmetro acumulador que foi sendo criado/alterado a cada iteração com os itens do array.
var resultados = times.reduce((acc, cur) => {
// cur[0] é a primeira letra de cada item do array
let k = cur[0].toLocaleUpperCase()
// Adiciona numa entrada existente ou cria uma nova
if (acc[k]) acc[k].push(cur)
else acc[k] = [cur]
return acc
}, {})
Convertendo os resultados de cada letra num objeto
Neste exemplo nós também vamos usar o reduce, exatamente da mesma forma como no exemplo anterior. O que vai mudar é a forma como o resultado vai ser entregue. Cada entrada do array resultado vai receber um objeto contendo dois campos, o primeiro chamado letra e o segundo chamado data com um array contendo os nomes dos times que começam com aquela letra.
var times = ["Palmeiras", "Flamengo", "Cruzeiro", "Internacional", "Fluminense", "Corinthians", "Athletico-PR","Atlético", "Fortaleza","São Paulo","América", "Botafogo", "Santos", "Goiás", "Red Bull Bragantino", "Coritiba", "Cuiabá", "Grêmio", "Vasco", "Bahia"];
resultados = Object.values(
times.reduce((acc, cur) => {
let primeraLetra = cur[0].toLocaleUpperCase();
if (!acc[primeraLetra]) {
acc[primeraLetra] = { letra: primeraLetra, data: [cur] };
} else {
acc[primeraLetra].data.push(cur);
}
return acc;
}, {})
);
console.log(resultados);
O método estático Object.values() retorna um array de objetos.
Perceba que neste exemplo cada entrada do array é um objeto contendo dois elementos, o primeiro é a letra e o segundo é um array com os times que começam com aquela letra. Perceba que este array resultados está fora da ordem alfabética. Para resolver isso, podemos ordenar os resultados utilizando o método sort e passando para este método um parâmetro que será uma função que vai ordenar os resultados de acordo com o campo "letra":
var times = ["Palmeiras", "Flamengo", "Cruzeiro", "Internacional", "Fluminense", "Corinthians", "Athletico-PR","Atlético", "Fortaleza","São Paulo","América", "Botafogo", "Santos", "Goiás", "Red Bull Bragantino", "Coritiba", "Cuiabá", "Grêmio", "Vasco", "Bahia"];
resultados = Object.values(
times.reduce((acc, cur) => {
let primeraLetra = cur[0].toLocaleUpperCase();
if (!acc[primeraLetra]) {
acc[primeraLetra] = { letra: primeraLetra, data: [cur] };
} else {
acc[primeraLetra].data.push(cur);
}
return acc;
}, {})
);
function ordenar(a,b) {
if (a.letra < b.letra)
return -1;
if (a.letra > b.letra)
return 1;
return 0;
}
console.log(resultados.sort(ordenar));
Se você quiser acessar a entrada referente a letra "A" pode utiilizar o método find e filtrar pelo campo letra de cada item do array:
console.log(resultados.find((x) => x.letra === "A"));
Utilizando Map
Este vai ser um exemplo um pouco mais complexo, vamos utiilzar o objeto Map. O objeto Map guarda coleções de elementos chave-valor. No exemplo a seguir o conteúdo da variável resultados é criada atrávés de uma arrow function. As arrow function existem desde a versão ES6 do ECMAScript.
var times = ["Palmeiras", "Flamengo", "Cruzeiro", "Internacional", "Fluminense", "Corinthians", "Athletico-PR","Atlético", "Fortaleza","São Paulo","América", "Botafogo", "Santos", "Goiás", "Red Bull Bragantino", "Coritiba", "Cuiabá", "Grêmio", "Vasco", "Bahia"];
let resultados = ((m, a) => (a.forEach(s => {
let a = m.get(s[0].toLocaleUpperCase()) || [];
m.set(s[0].toLocaleUpperCase(), (a.push(s), a));
}), m))(new Map(), times);
console.log(resultados.get("A"));
Para acessar o primeiro elemento que começa com a letra "A":
console.log(resultados.get("A")[1]);
Vamos entender como funcionou a ordenação utilizando o objeto Map. Perceba que fizemos a iteração dos itens do array utilizando forEach.
let resultados = ((m, a) => (a.forEach(s => {
//Codigo a ser executado
}), m))(new Map(), times);
Antes do nosso forEach existem dois parâmetros, o "m" e o "a". O primeiro parâmetro é o acumulador, ele recebe um novo objeto Map que vai ser criado, já o segundo parâmetro são os dados, que no nosso caso é o array de times.
A primeira instrução que executamos foi verificar se existe em nosso objeto Map um elemento com uma chave igual a letra atual. Para retornamos um elemento do Map de acordo com uma chave utilizamos o método GET. Neste exemplo a variável A pode ser o conteudo da chave com a letra atual, ou um array vazio caso não tenha sido localizado.
let resultados = ((m, a) => (a.forEach(s => {
let a = m.get(s[0].toLocaleUpperCase()) || [];
//Resto do código
}), m))(new Map(), times);
Por fim precisamos atualizar a chave ou criar caso não exista. Para adicionar novos elementos ao objeto Map utilizamos o método SET. O método SET recebe dois parâmetros, o primeiro é o nome da chave, que em nosso caso é uma letra, o segundo parâmetro é o conteúdo desta chave, que será o array com os nomes dos times que começam com a mesma letra da chave atual. Da mesma forma que fizemos nos exemplos anteriores, depois de recuperar o array da letra atual fazemos um push para adicionar o novo elemento.
let resultados = ((m, a) => (a.forEach(s => {
let a = m.get(s[0].toLocaleUpperCase()) || [];
m.set(s[0].toLocaleUpperCase(), (a.push(s), a));
}), m))(new Map(), times);
Além dos métodos GET e SET, temos o método HAS para verificar se existe uma determinada chave, ele retorna TRUE em caso de sucesso e FALSE se não localizar a chave informada.
console.log(resultados.has("X"));
E para excluir uma chave você pode utilizar o método DELETE.
console.log(resultados.size);
resultados.delete("C");
console.log(resultados.size);
No exemplo anterior utilizamos o método SIZE para verificar a quantidade de elementos de nosso objeto Map.
Para apagar todo o conteúdo de um objeto Map podemos usar o método CLEAR()
resultados.clear();