How to filter multiple items within an array

Asked

Viewed 186 times

8

I have the following JSON:

{
    "Empresas": [{
      "Name": "DIST",
      "Portais": [{
        "title": "PRD",
        "tags": ["PRD", "DIST1"]
      },{
        "title": "HIST",
        "tags": ["PCP", "DIST2", "DIST3"]
        }]
    },
     {"Name": "LUZ",
       "Portais": [{
         "title": "QA",
         "tags": ["QA", "LUZ"]
       }]
     }]
}

In my navbar has a search bar, which at the moment is only filtering a tag, I wanted to search for several. Ex: "PCP DIST2".

Follow part of the code below:

this.portais.forEach( (e: Empresa) => {
    var novoElemento = Object();
    novoElemento.Name = e.Name;

    novoElemento.Portais = e.Portais.filter(item => item.tags.find(i => i.toLocaleLowerCase().indexOf(val.toLowerCase())> -1));

    this.output_portais.push(novoElemento);

});

The variable 'val' refers to the value entered in the search field.

  • Filter more than one, you want to search both (AND) or any of the typed (OR)?

1 answer

4


If typed "PCP DIST2", you want to Filter the elements you have both tags "PCP" and "DIST2", or may be the elements that have at least one of them?


Regardless of the criteria, one way to solve is to first break the tags in an array, using split (but first use toLowerCase() to turn them into tiny, because I saw that you want to do the consultation case insensitive, besides trim() to remove the spaces from the beginning and end of the string, otherwise the split return empty strings in the array):

let val = 'PCP DIST2';
let valores = val.trim().toLowerCase().split(' ');
console.log(valores); // ["pcp", "dist2"]

// se estiver separado por mais de um espaço, use:
val = '  PCP    DIST2  ';
valores = val.trim().toLowerCase().split(/\s+/);
console.log(valores); // ["pcp", "dist2"]

Then we have to decide the criterion. If we want to verify that both tags are in the array, we have to use every (which checks that all elements meet the criteria). But if we want any of the tags ( but not necessarily all) are in the array, we use some (that checks if any of the elements meet the criterion). And to check if the element is in the array, we use includes. Some examples:

let val = 'PCP DIST2';
let valores = val.trim().toLowerCase().split(' ');

// verificando se todas as tags estão no array
// array contém PCP e DIST2
console.log(valores.every(valor => ['PCP', 'ABC', 'DIST2'].map(s => s.toLowerCase()).includes(valor))); // true 
// array contém PCP, mas não DIST2
console.log(valores.every(valor => ['PCP', 'ABC', 'DIST1'].map(s => s.toLowerCase()).includes(valor))); // false 
// array não contém PCP nem DIST2
console.log(valores.every(valor => ['PC', 'ABC', 'DIST1'].map(s => s.toLowerCase()).includes(valor))); // false 

// verificando se alguma das tags está no array
// array contém PCP, mas não DIST2
console.log(valores.some(valor => ['PCP', 'ABC', 'DIST1'].map(s => s.toLowerCase()).includes(valor))); // true 
// array contém DIST2, mas não PCP
console.log(valores.some(valor => ['PC', 'ABC', 'DIST2'].map(s => s.toLowerCase()).includes(valor))); // true 
// array não contém PCP nem DIST2
console.log(valores.some(valor => ['PC', 'ABC', 'DIST1'].map(s => s.toLowerCase()).includes(valor))); // false 

That is, for all search values, I check that all (or any of them, depending on the criteria) are in the tag array.

Of course you could use indexOf, which returns the index of the element (or -1 if it does not exist in the array), but as you only want to know if the element exists or not, use includes is more direct because it returns only true or false.

Another detail is that filter returns an empty array if no element satisfies the criterion is found. Then you should check that the returned array is empty before adding it to the results.

Then in your case:

// para verificar se todas as tags estão no array
this.portais.forEach( (e: Empresa) => {
    var novoElemento = Object();
    novoElemento.Name = e.Name;
    novoElemento.Portais = e.Portais.filter(item => valores.every(valor => item.tags.map(s => s.toLowerCase()).includes(valor)));
    if (novoElemento.Portais.length > 0) { // só insiro se encontrou algum
        this.output_portais.push(novoElemento);
    }
});

// para verificar se alguma das tags está no array
this.portais.forEach( (e: Empresa) => {
    var novoElemento = Object();
    novoElemento.Name = e.Name;
    novoElemento.Portais = e.Portais.filter(item => valores.some(valor => item.tags.map(s => s.toLowerCase()).includes(valor)));
    if (novoElemento.Portais.length > 0) { // só insiro se encontrou algum
        this.output_portais.push(novoElemento);
    }
});

Also note the use of map to have the whole tag array in lowercase.

  • 1

    My intention was to filter the elements that have both tags, thank you very much for the explanation, I changed here and it worked exactly as I wanted :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.