Thanh N
Thanh N

Reputation: 23

How to filter out more than one object from an array using multiple text string condition

I'm trying to write a test and I have an array of objects which look something like this:

menuOfProducts: [ 
  { text: 'Product One',
    selector: '#product-one #productone',
    path: 'productone' },
  { text: 'Product Two',
    selector: '#product-two #producttwo',
    path: 'shop/catalog/producttwo' },
  { text: 'Product Three',
    selector: '#product-three #productthree',
    path: 'shop/catalog/productthree' },
  { text: 'Product Four',
    selector: '#product-four #productfour',
    path: 'shop/catalog/productfour' },
  { text: 'Product Five',
    selector: '#product-five #productfive',
    path: 'shop/catalog/productfive' }
]

What I would like to do is filter out a couple of the objects and return the rest.

So far I have tried to use .filter() to filter out one of the objects which works fine. However it may be required to filter out more than one product by text. This is what I have now:

if (environment === 'test') {
  menuOfProducts = menuOfProducts.filter(function (option) {
    return option.text !== 'Product Two';
  });
}

And using this filter I get the correct array returned minus "Product Two":

[ 
  { text: 'Product One',
    selector: '#product-one #productone',
    path: 'productone' },
  { text: 'Product Three',
    selector: '#product-three #productthree',
    path: 'shop/catalog/productthree' },
  { text: 'Product Four',
    selector: '#product-four #productfour',
    path: 'shop/catalog/productfour' },
  { text: 'Product Five',
    selector: '#product-five #productfive',
    path: 'shop/catalog/productfive' }
]

But as mentioned above I would like to now filter multiple objects out by text. And was wondering how can I approach this? I have tried passing in another condition in the filter like this:

if (environment === 'test') {
  menuOfProducts = menuOfProducts.filter(function (option) {
    return option.text !== 'Product Two' || 'Product Three';
  });
}

But then I get the array returned with ALL object and nothing is filtered out. Any help would be greatly received. Many thanks in advance

Upvotes: 0

Views: 56

Answers (3)

yunzen
yunzen

Reputation: 33439

You are using the logic operators wrong. You use it in a way that a real person might understand, but a computer has to be treated differently.

Your logic term: option.text !== 'Product Two' || 'Product Three';
I shorten this to A !== B || C
The operator precedence makes that equivalent to this: (A !== B) || C
This translates to 'A not equal to B else C'
So if A !== B is true, the whole term has the value of true (because true || anything is always true). But if A !== B is false, the whole term will evaluated to V (because false || anything is always anything) So you will either have the value true of C, C is the string 'Product Three' which is a truthy value.
In the end your full term option.text !== 'Product Two' || 'Product Three' will always be true, thus nothing is filtered out

What you need is A !== B && A !== C, which is evaluated like (A !== B) && (A !== C)
In this case the term will only be true id option.text is neither equal to 'Product Two' nor to 'Product Three'
So your term must be option.text !== 'Product Two' && option.text !== 'Product Three'

console.clear()
menuOfProducts = [ 
  { text: 'Product One',
    selector: '#product-one #productone',
    path: 'productone' },
  { text: 'Product Two',
    selector: '#product-two #producttwo',
    path: 'shop/catalog/producttwo' },
  { text: 'Product Three',
    selector: '#product-three #productthree',
    path: 'shop/catalog/productthree' },
  { text: 'Product Four',
    selector: '#product-four #productfour',
    path: 'shop/catalog/productfour' },
  { text: 'Product Five',
    selector: '#product-five #productfive',
    path: 'shop/catalog/productfive' }
]

menuOfProducts = menuOfProducts.filter(function (option) {
  return option.text !== 'Product Two' &&  option.text !== 'Product Three';
});

console.log(menuOfProducts)

What is operator precedence

The MDN documentation says:

Operator precedence determines the way in which operators are parsed with respect to each other. Operators with higher precedence become the operands of operators with lower precedence.

This means that we have a sorted list of all possible operators and the operators which are higher in this list are parsed before the lower ones.
We can visualize this with the use of parentheses [( and )] which are the Grouping operators and on top of the operator precedence list.

One example A = B + C
This is like A = (B + C), because the Assignment operator (single =) has precedence 3 and the Addition operator (+) has precedence 13, hence being parsed before the =

Your term was A !== B || C. Let's take a look at the precedence list

| precedence | operator name     | operator |
| 10         | Strict Inequality |   !==    |
|  5         | Logical OR        |   ||     |

The Strict Inequality operator has higher priority than the Logical OR operator. So A !== B || C is similar to (A !== B) || C All the operators

Upvotes: 0

adiga
adiga

Reputation: 35253

You are getting all the values returned because 'Product Three' is a truthy value

Use the Logical AND operator like this:

if (environment === 'test') {
  menuOfProducts = menuOfProducts.filter(function (option) {
    return option.text !== 'Product Two' && option.text !== 'Product Three';
  });
}

If you have a multiple option.text to filter, you could create an array of those values and use includes:

if (environment === 'test') {
  menuOfProducts = menuOfProducts.filter(function(option) {
    return !['Product Two', 'Product Three'].includes(option.text);
  });
}

Upvotes: 2

Arpit Agrawal
Arpit Agrawal

Reputation: 1180

You need to do this:

if (environment === 'test') {
  menuOfProducts = menuOfProducts.filter(function (option) {
    return option.text !== 'Product Two' && option.text !== 'Product Three';
  });
}

Upvotes: 2

Related Questions