28

I have this array:

var fruits = ['Apple', 'Banana', 'Orange', 'Celery'];

And I use Lodash's remove like so:

_.remove(fruits, function (fruit) {
  return fruit === 'Apple' || 'Banana' || 'Orange';
})

The result is ['Apple', 'Banana', 'Orange', 'Celery'], while I expected it to be ['Apple', 'Banana', 'Orange']. Why is this so?

bard
  • 2,592
  • 6
  • 31
  • 45

5 Answers5

57

Because when fruit is "Celery", you are testing:

"Celery" === 'Apple' || 'Banana' || 'Orange'

which evaluates to

false || true || true

which is true.

You can't use that syntax. Either do it the long way around:

_.remove(fruits, function (fruit) {
  return fruit === 'Apple' || fruit === 'Banana' || fruit === 'Orange'
});

or test for array membership:

_.remove(fruits, function (fruit) {
  return _.indexOf(['Apple', 'Banana', 'Orange'], fruit) !== -1
});

This is not limited to JavaScript, and is in fact a common mistake (e.g. this question)

Community
  • 1
  • 1
Amadan
  • 179,482
  • 20
  • 216
  • 275
36

You can use the method _.pull from lodash 2.0 and up

var fruits = ['Apple', 'Banana', 'Orange', 'Celery'];

_.pull(fruits, 'Apple', 'Banana', 'Orange'); // ['Celery']

document.write(fruits);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.js"></script>
gustavogelf
  • 360
  • 3
  • 4
12

If you want to remove a set of items from another set, there are set operations intended specifically for that. Lodash has https://lodash.com/docs/4.17.2#difference which takes two array parameters A and B and will return another array which contains all of the elements of A which are not in B.

In your case, you could write

const fruits = ['Apple', 'Banana', 'Orange', 'Celery'];
const filteredFruits = _.difference(fruits, ['Apple', 'Banana', 'Orange']);

which will result in ['Celery'].

Mouscellaneous
  • 2,424
  • 3
  • 26
  • 37
10

The problem isn't with Lo-Dash; your problem is with your conditional within your callback function. This:

return fruit === 'Apple' || 'Banana' || 'Orange';

Is not correct. You need to actually compare fruit with each string:

return fruit === 'Apple' || fruit === 'Banana' || fruit === 'Orange';

Or, you can use another Lo-Dash function to make it a little more compact:

_.remove(fruits, function (fruit) {
  return _.contains(['Apple', 'Banana', 'Orange'], fruit);
})

Note: In the latest versions of Lo-Dash the _.contains function is deprecated. Please use _.includes

Hristo Eftimov
  • 11,041
  • 12
  • 49
  • 74
ncksllvn
  • 5,449
  • 2
  • 20
  • 28
2

Use an array of values that you'd like to compare against, and check for a returned index greater than -1. This indicates the value evaluated was found in the collection.

_.remove( fruits, function ( fruit ) {
  return _.indexOf( [ "Apple", "Banana", "Orange" ], fruit ) >= 0;
});

Alternatively you could use lo-dash's _.contains method to get a boolean response.

The problem with the approach you took was that you weren't comparing fruit against each one of those strings; instead, the only comparison taking place was fruit against "Apple", after that you were coercing strings all on their own.

Non-empty strings coerce to true (!!"Banana"), and as such are truthy. Therefore, the following condition will always short-circuit at "Banana" (unless fruit strictly equals "Apple"), returning true:

return fruit === "Apple" || 'Banana' || "Orange";
Sampson
  • 259,174
  • 73
  • 529
  • 557
  • 1
    Note: Lodash no longer uses the function ``_.contains``. It used to be an alias for ``_.includes``, but is no longer as of 3.10.0. Use ``_.includes`` instead. https://github.com/mgonto/restangular/issues/1298 – Alex Johnson Nov 28 '16 at 18:49