some takes in a callback function where you can write your own logic to determine if an array contains some element which matches the conditions you wrote.
includes does a generic equalTo comparison on every element and will return true if at least one element in the array is equal to the value to find.
Videos
I understand how each of these methods work, but they all seem to have similar functions to one another, and I'm not sure as to what situation might call for what method.
tl;dr: NaN is treated differently:
[NaN].indexOf(NaN) > -1isfalse[NaN].includes(NaN)istrue
From the proposal:
Motivation
When using ECMAScript arrays, it is commonly desired to determine if the array includes an element. The prevailing pattern for this is
if (arr.indexOf(el) !== -1) { ... }with various other possibilities, e.g.
arr.indexOf(el) >= 0, or even~arr.indexOf(el).These patterns exhibit two problems:
- They fail to "say what you mean": instead of asking about whether the array includes an element, you ask what the index of the first occurrence of that element in the array is, and then compare it or bit-twiddle it, to determine the answer to your actual question.
- They fail for
NaN, asindexOfuses Strict Equality Comparison and thus[NaN].indexOf(NaN) === -1.Proposed Solution
We propose the addition of an
Array.prototype.includesmethod, such that the above patterns can be rewritten asif (arr.includes(el)) { ... }This has almost the same semantics as the above, except that it uses the SameValueZero comparison algorithm instead of Strict Equality Comparison, thus making
[NaN].includes(NaN)true.Thus, this proposal solves both problems seen in existing code.
We additionally add a
fromIndexparameter, similar toArray.prototype.indexOfandString.prototype.includes, for consistency.
Further information:
SameValueZeroalgorithmStrict Equality Comparisonalgorithm
Technically
NaNorundefinedwill not be findable when usingindexOf.
[NaN].indexOf(NaN) // => -1 (not found)
[NaN].includes(NaN) // => true
[undefined].indexOf(undefined) // => -1 (not found)
[undefined].includes(undefined) // => true
includesalso is of no use if you want to know at where index the element was found.While searching a string,
includesaccepts regexpes as opposed to indexOf
Readability
arr.includes('searchedElement')does what it says and it is obvious that it returns aboolean.- while
arr.indexOf('searchedElement') !== -1to know if something exists in a string or an array is less more readable.
Performances
According to this article on the subject there are no noticeable difference although includes may be a very little bit slower.
History
indexOf was created way before includes.
Browser support
indexOf=> 97.08%includes=> 97.51%
So both can be used safely.
Vanilla JS
const found = array1.some(r=> array2.includes(r))
How it works
some(..) checks each element of the array against a test function and returns true if any element of the array passes the test function, otherwise, it returns false. includes(..) both return true if the given argument is present in the array.
vanilla js
/**
* @description determine if an array contains one or more items from another array.
* @param {array} haystack the array to search.
* @param {array} arr the array providing items to check for in the haystack.
* @return {boolean} true|false if haystack contains at least one item from arr.
*/
var findOne = function (haystack, arr) {
return arr.some(function (v) {
return haystack.indexOf(v) >= 0;
});
};
As noted by @loganfsmyth you can shorten it in ES2016 to
/**
* @description determine if an array contains one or more items from another array.
* @param {array} haystack the array to search.
* @param {array} arr the array providing items to check for in the haystack.
* @return {boolean} true|false if haystack contains at least one item from arr.
*/
const findOne = (haystack, arr) => {
return arr.some(v => haystack.includes(v));
};
or simply as arr.some(v => haystack.includes(v));
If you want to determine if the array has all the items from the other array, replace some() to every()
or as arr.every(v => haystack.includes(v));
It's true that object lookup occurs in constant time - O(1) - so using object properties instead of an array is one option, but if you're just trying to check whether a value is included in a collection, it would be more appropriate to use a Set, which is a (generally unordered) collection of values, which can also be looked up in linear time. (Using a plain object instead would require you to have values in addition to your keys, which you don't care about - so, use a Set instead.)
const set = new Set(['foo', 'bar']);
console.log(set.has('foo'));
console.log(set.has('baz'));
This will be useful when you have to look up multiple values for the same Set. But, adding items to the Set (just like adding properties to an object) is O(N), so if you're just going to look up a single value, once, there's no benefit to this nor the object technique, and you may as well just use an array includes test.
Updated 04/29/2020
As the commenter rightly pointed out it would seem V8 was optimizing out the array includes calls. An updated version that assigns to a var and uses it produces more expected results. In that case Object address is fastest, followed by Set has and in a distant third is Array includes (on my system / browser).
All the same, I do stand by my original point, that if making micro-optimizations it is worth testing assumptions. Just make sure your tests are valid ;)
Original
Well. Despite the obvious expectation that Object address and Set has should outperform Array includes, benchmarks against Chrome indicate that implementation trumps expectation.
In the benches I ran against Chrome Array includes was far and away the best performer.
I also tested locally with Node and got more expected results. In that Object address wins, followed closely by Set has, then Array includes was marginally slower than both.
Bottom line is, if you're making micro-optimizations (not recommending that) it's worth benchmarking rather than assuming which might be best for your particular case. Ultimately it comes down to the implementation, as your question implies. So optimizing for the target platform is key.
Here's the results I got:
Node (12.6.0):
ops for Object address 7804199
ops for Array includes 5200197
ops for Set has 7178483
Chrome (75.0):
https://jsbench.me/myjyq4ixs1/1
