How to filter an object array using other object array?

Asked

Viewed 573 times

1

I am trying to create a filter of recent products that are searched (when starting to type, it already appears the products found, as if it were a "like"), I have a global variable that is an array, and from it I save in another array the data of Return item, 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 variable this. $myGlobalStuff.searchArray has more than one item in my articles_array2 always empty when function is called.

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

https://codepen.io/haykou/pen/gdZQJZ

As can be seen if an item is added to the variable

searchArray: ['dermodex']

Filter no longer works.

  • Create a snippet with functional code to make it easier. Is this code you posted a computedProperty? A method?

  • I’ll try to improve, it’s a computed

1 answer

3


I tried to play your code in the snippet below. From what I could notice there are two more serious errors that prevent the operation.

  1. On the 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 not saving the result in a variable.

  2. On the line:

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

    The method String.indexOf() looks 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 the Array.indexOf() which has a similar function, but looks for the occurrence within an array. So I think what you want to use on that line is:

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

EDIT

I modified the code to behave like a LIKE and as a OR. The function was thus:

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 strings, as it is used more than once in the code. This function could be elsewhere, outside the computedProperty (maybe I should even).


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

Sanitizes 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 the elements whose function returns a value Truthy (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 to function in the elements of Array and returns true if any of the function returns are Truthy. I mean, it works like a OR (and the Array.every(function) functions as a 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 has been found 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 a binary exactly opposite of 0 (See about complement of 2).

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

    To understand the >>> 0 see this reply from Soen

    So if you apply one NOT torque (with the operator ~), any number other than -1 shall be !== 0 that is a value Truthy and -1 will be false. Then, in the conversion to boolean:

    x.indexOf(y) !== -1
    

    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>

<div id="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>

  • only a doubt, in the codepen that I did, the function was functioning as a "like", inserting only a few characters in searchArray, it already returned me the result. I can do the same with the adjustments you made?

  • Ah ok.. I need to modify the code then. I won’t be able to do it now, but as soon as I do I warn you here

  • quiet, I’m trying to hit here

  • I modified the code to work as a LIKE and a OR. The session EDIT contains the full explanation of the modifications

Browser other questions tagged

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