Executando verificação de segurança...
3

Como filtrar QUALQUER array de objetos no JavaScript

Olá pessoas!

Recentemente me deparei com um problema no meu estágio, onde eu precisava realizar uma filtragem de um array de objetos, dado um determinado input.

Mesmo depois de inúmeras pesquisas, não encontrei um método que atendia minha necessidade, pois, nenhum atuava de maneira genérica. Entretanto, as pesquisas me fizeram aprender o suficiente para que eu mesmo desenvolve-se uma solução.

Dessa maneira, gostaria de compartilhar com vocês o meio de utilizei prara isso!

Definindo estrutura de dados

Primeiro de tudo, é necessário definir o tipo de estrutura de dados mais genérica que iremos trabalhar.

No meu caso, eu recebi um array com vários objetos contendo informações pessoais dos usuários.

// Array de Objetos
const arr = [
    {
        nome: "Xandy",
        idade: 21,
        nacionalidade: "Brasil",
        cargo: "Programador"
    },
    {
        nome: "Marcela",
        idade: 21,
        nacionalidade: "Portugal",
        cargo: "Engenheira de Software"
    },
    {
        nome: "João Victor",
        idade: 35,
        nacionalidade: "Espanha",
        cargo: "Arquiteto"
    },
    {
        nome: "Sophia",
        idade: 29,
        nacionalidade: "Brasil",
        cargo: "Astronauta"
    },
];

Criando a função

A partir de uma entrada, eu preciso encontrar qualquer resultado semelhante em cada item de cada um dos objetos do array.

Para isso, três funções básicas do JavaScript serão utilizadas, sendo elas: include, map e filter.

// Função para filtrar
const Filter = (event) => {
    
    return arr.filter(obj => {
        
        return Object.values(obj).map(item => {
            
              return (item === null ? "" : item.toString().includes(event.target.value.toString()));
            
        }).includes(true);
        
    });
    
}

Explicação

Para melhor compreensão do que foi realizado, vamos dissecar a função de dentro para fora.

Include

Para isso, é necessário entender o método mais interno da função: o include.

O método includes() determina se um array inclui um determinado valor entre suas entradas, retornando true ou false conforme apropriado.

Dessa maneira, é possível analisar o valor de um item do objeto e comparar com a entrada dada pelo usuário, retornando um boolean para a avaliação feita.

Caso o valor seja nulo, ele retorna uma string vazia e, caso exista um valor no item analisado, o mesmo é convertido para uma string e comparado com o input do usuário, retornando true se obtiverem semelhança.

return (item === null ? "" : item.toString().includes(event.target.value.toString()));

Map

Posto isso, é necessário analisar cada um dos itens do objeto. Para isso, realiza-se um map nos valores do objeto, fazendo com que a função apresentada anterior seja executada para cada um.

O método map() cria uma nova matriz preenchida com os resultados da chamada de uma função fornecida em cada elemento da matriz de chamada.

return Object.values(obj).map(item => {
            
              return (item === null ? "" : item.toString().includes(event.target.value.toString()));
            
        }).includes(true);

É interessante notar que, no final da função map(), também é realizado uma verificação com o include. O motivo será explicado a partir da próxima e última função.

Filter

Após realizar a verificação em cada um dos itens de um objeto, agora fica mais simples. É necessário filtar os objetos que possuem um item semelhante ao input do usuário. Sendo assim, a função filter resolve o problema.

O método filter() cria uma cópia superficial de uma parte de uma determinada matriz, filtrada apenas para os elementos da matriz fornecida que passam no teste implementado pela função fornecida.

Assim, caso algum dos objetos possua um item semelhante ao input do usuário, ele irá retornar um array com pelo meno um true, graças ao include dentro do objeto. Em seguida, um segundo include é realizado nesse array (aquele citado no método map), checando se existe algum valor true dentro do mesmo. Caso exista um valor true, o objeto passa no teste do filter e é salvo dentro do novo array.

Dessa maneira, obtêmos nossa função com toda sua lógica aplicada:

return arr.filter(obj => {
        
        return Object.values(obj).map(item => {
            
              return (item === null ? "" : item.toString().includes(event.target.value.toString()));
            
        }).includes(true);
        
    });

Aqui podemos ver um exemplo da utilização do filtro no array de objetos apresentado no inicio do artigo, mostrando que foi retornado os dois objetos que possuem Brasil como nacionalidade.

Exemplo de utilização de filtro

O código completo utilizado pode ser encontrado no meu repositório no GitHub

Considerações finais

É importante lembrar que, existem inúmeras maneiras de se solucionar um mesmo problema. Essa foi a solução que eu consegui encontrar no tempo que me foi dado, e ela me serviu bem.

Se você tem ideia de outra maneira mais eficiente para realizar a mesma tarefa, deixa aí nos comentários que eu vou adorar aprender!!!

Espero ter contribuido de alguma maneira.
Abraços! 🐢

Carregando publicação patrocinada...
2

Primeiramente achei a solução muito boa, então parabéns. Agora uma pequena correção.

Nessa linha recomendo mudar a arbodagem. Ao invés do código orignal, como está abaixo

    return (item === null ? "" : item.toString().includes(event.target.value.toString()));

Utilizar essa implementação

    return (item === null ? "" : item.toString() === event.target.value.toString());

Explico o motivo da mudança. O .include() vai verificar se determinado valor existe em um array, nesse caso digamos que a gente tivesse o seguinte array

    const array = [
        {
            nome: "Nathally",
            idade: 21
        },
        {
            nome: "Nathalia",
            idade: 23
        },
        {
            nome: "Nath",
            idade: 35
        }
    ]

Neste caso, se você usar o código original e buscar somente por "Nath" ele retornará os 3 elementos, pois vai considerar que a string "Nath" existe na propriedade nome dos 3 objetos, o que é verdade. Então para evitar esse "erro", boto entre aspas pois não entendi se esse comportamento é esperado ou não, com a sugestão de correção ele retornará apenas o elemento que de fato apresenta a propriedade nome com o valor "Nath".

O código final sugerido fica assim

    const Filter = (event) => {

        return arr.filter(obj => {

            return Object.values(obj).map(item => {

                  return (item === null ? "" : item.toString() === event.target.value.toString());

            }).includes(true);

        });

    }

Espero que ajude :)

2

Oi Nathsouza!

Realmente, a sua correção se aplica bem no que foi passado, sou muito grato por ter percebido esse equívoco!

Na minha aplicação no sistema real, acabou por ser um live-search que filtra com o event "keypress". Então ele me mostra resultados em tempo real, para cada tecla inserida, não precisando, necessáriamente, mostrar apenas os resultados em que os valores sejam estritamente iguais. Dessa maneira, precisa apenas conter uma parcela do valor real para que já sejam mostrados para o usuário.

Porém, como no exemplo não foi citado isso, e o exemplo é diferente da aplicação real, a sua correção está certíssima!

Muito obrigado!

1

Olá Nathsouza.

Na verdade, a depender do contexto o seu código seria o mais indicado, pois ele estaria comparando se o valor passado é igual e do mesmo tipo. Mas no caso do primeiro código, se estivesse buscando uma lista de itens, acredito que seria a melhor forma de consultar.