How to filter an array of objects using other array of objects?

1

I'm trying to create a filter for recent products that are fetched (when I start typing, it already shows the found products, like a "like"), I have a global variable that is an array, and from there I except in another array the return item data, as can be seen in the example below:

produtosFiltrados: function() {

let articles_array = this.produtos;
let searchString = this.$myGlobalStuff.searchArray; //Global

searchString.map(searchString => searchString.trim().toLowerCase());

  articles_array2 = articles_array.filter(function(item) {
    if (item.nome.toLowerCase().indexOf(searchString) !== -1) {
      return item;
    }
  });

  return articles_array2;
}

But when my this. $ myGlobalStuff.searchArray variable has more than one item my articles_array2 always stays empty when the function is called.

To better understand the problem, I simulated the problem at the link below:

link

How can it be seen if an item is added to the variable

searchArray: ['dermodex']

The filter no longer works.

    
asked by anonymous 19.09.2018 / 22:09

1 answer

3

I tried to reproduce your code in the snippet below. As far as I can tell you have two more serious errors that prevent it from working.

  • On line:

    searchString.map(searchString => searchString.trim().toLowerCase());
    

    The method Array.map() returns a new array with the callback result applied to all array elements. In your case, you are doing the calculation and you are not saving the result to a variable.

  • On line:

    item.nome.toLowerCase().indexOf(searchString)
    

    The method String.indexOf() searches for an occurrence of a substring in another string, but by the logic of your code it seems to me that you want to use Array.indexOf() " that has similar function, but looks for the occurrence inside an array. So I believe that what you want to use in this line is:

    searchString.indexOf(item.nome.toLowerCase())
    
  • EDIT

    I have modified the code to behave as a LIKE and as a OR . The function looks like this:

    function produtosFiltrados() {
        let sanitize = str => str.trim().toLowerCase();
        let strings = this.myGlobalStuff.searchArray.map(sanitize);
    
        return this.produtos.filter(function(item) {
            let nome = sanitize(item.nome);
            return strings.some(str => ~nome.indexOf(str));
        });
    }
    

    Explanation of the code:

    let sanitize = str => str.trim().toLowerCase();
    

    It only saves the function that sanitizes the string, since it is used more than once in the code. This function could be somewhere else, out of computedProperty (maybe it should be).

    let strings = this.myGlobalStuff.searchArray.map(sanitize);
    

    Sanitize all search terms before using them.

    return this.produtos.filter(function(item) {
        let nome = sanitize(item.nome);
        return strings.some(str => ~nome.indexOf(str));
    });
    

    This is where it all happens:

    • this.produtos.filter(...) will return an array only with elements whose function returns a truthy value. (which when converted to boolean is true). Ex .:

      // Filtra apenas os números ímpares
      [0, 1, 2, 3].filter(x => x % 2)  // [1, 3]
      
    • strings.some(...) , the method Array.some(function) applies function to elements of Array and returns true if any of the function returns is truthy . That is, it works as a OR (and Array.every(function) / a> works like AND , just for curiosity). Ex.:

      // Testa se tem algum número ímpar no array
      [0, 1, 2, 3].some(x => x % 2)  // true
      [0, 2, 4, 6].some(x => x % 2)  // false
      
    • ~nome.indexOf(str) , the method String.indexOf(substring) returns the position of the substring inside the string or -1 if the substring does not exist inside the string. That said, to know if the substring was found, we just test if x.indexOf(y) !== -1 , however, as a curiosity, if you convert -1 to a binary representation you will realize that it is an exactly opposite binary of 0 (See about 2's complement ).

      (-1 >>> 0).toString(2) // "11111111111111111111111111111111"
      (0).toString(2)        // "00000000000000000000000000000000"
      

      To understand >>> 0 see this SOEn response

      So if you apply a NOT binary (with the ~ operator), any number other than -1 will !== 0 which is a value truthy and -1 will be fake. Then, in the conversion to boolean :

      x.indexOf(y) !== -1
      

      It is equivalent to:

      ~x.indexOf(y)
      

    Then the function:

    return this.produtos.filter(function(item) {
        let nome = sanitize(item.nome);
        return strings.some(str => ~nome.indexOf(str));
    });
    

    Means:

      

    Filter all products whose name contains any of the search terms.

    Code working:

    new Vue({
        el: '#app',
        data: {
            myGlobalStuff: {  // fake da Global
                searchArray: ["1", "5"]
            },
            produtos: [
                {id: 1, nome: "Produto 1"},
                {id: 2, nome: "Produto 2"},
                {id: 3, nome: "Produto 3"},
                {id: 4, nome: "Produto 4"},
                {id: 5, nome: "Produto 5"},
                {id: 6, nome: "Produto 6"},
            ]
        },
        computed: {
            produtosFiltrados: function() {
                // Salva a função apenas por praticidade
                let sanitize = str => str.trim().toLowerCase();
                let strings = this.myGlobalStuff.searchArray.map(sanitize);
                
                return this.produtos.filter(function(item) {
                    let nome = sanitize(item.nome);
                    return strings.some(str => ~nome.indexOf(str));
                });
            }
        }
    });
    <script src="https://cdn.jsdelivr.net/npm/vue"></script><divid="app">
      <strong>Produtos</strong>
      <ul>
        <li v-for="p in produtos" :key="p.id">
          [{{ p.id }}] {{ p.nome }}
        </li>
      </ul>
      <strong>Produtos Filtrados</strong>
      <ul>
        <li v-for="p in produtosFiltrados" :key="p.id">
          [{{ p.id }}] {{ p.nome }}
        </li>
      </ul>
    </div>
        
    19.09.2018 / 23:04