Array.Prototype.Includes VS. Array.Prototype.Indexof

Array.prototype.includes vs. Array.prototype.indexOf

tl;dr: NaN is treated differently:

  • [NaN].indexOf(NaN) > -1 is false
  • [NaN].includes(NaN) is true

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, as indexOf uses Strict Equality Comparison and thus [NaN].indexOf(NaN) === -1.

Proposed Solution


We propose the addition of an Array.prototype.includes method, such that the above patterns can be rewritten as

if (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 fromIndex parameter, similar to Array.prototype.indexOf and String.prototype.includes, for consistency.


Further information:

  • SameValueZero algorithm
  • Strict Equality Comparison algorithm

Array indexOf() vs includes() perfomance depending on browser and needle position

I made a test using array with 10 000 numeric values, here is results:

Chrome:

  • beginning

    • includes (22,043,904 ops/sec)
    • indexOf (136,512,737 ops/sec)
  • middle

    • includes (8,361 ops/sec)
    • indexOf (31,296 ops/sec)
  • ending

    • includes (4,018 ops/sec)
    • indexOf (95,221 ops/sec)

Firefox:

  • beginning

    • includes (34,087,623 ops/sec)
    • indexOf (33,196,839 ops/sec)
  • middle

    • includes (84,880 ops/sec)
    • indexOf (86,612 ops/sec)
  • ending

    • includes (25,253 ops/sec)
    • indexOf (14,994 ops/sec)

So, indexOf() in Chrome works much faster than includes() in all positions.

In Firefox both indexOf() and includes() works almost similar.

Why do includes() and indexOf() work differently with searching NaN?

Basically, Array.prototype.indexOf uses a different equality algorithm than Array.prototype.includes.

Array.prototype.indexOf ends up using Number::equal. Which does:

  1. If x is NaN, return false.
  2. If y is NaN, return false.

While Array.prototype.includes ends up using Number::sameValueZero

Which does:

  1. If x is NaN and y is NaN, return true.

As to why does the spec allow a comparison of two different NaNs to be true, well that's up to the TC. I can only guess they made it this way as it's a convenient way to check if there's a NaN in the array.

JavaScript indexOf vs Array.prototype.indexOf IE compatibility error

This question has come up many times, another Stackoverflow thread can be found here: How to fix Array indexOf() in JavaScript for Internet Explorer browsers

Several people on different threads have recommended using this code from MDC. This appears to be the code:

if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length >>> 0;

var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;

for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}

Just run this code before making any calls indexOf()

Array.prototype.includes() wildcard options?

As others mentioned, you cannot use regex with Array.includes(). The simplest way would be to use a some() or every() (depending on what do you want to achieve) instead:

var ReviewtagFilter = function() {
let reviewsTagged = [];
let tagLevel = "_easy";
let tagTopic = /^-/;

reviewsTagged = cards.filter(
data => data.some(tag => tag.match(tagTopic)) && data.tags.includes(tagLevel)
);
console.log(reviewsTagged)
};

Bug in Array.prototype.includes?

It's not a bug in includes. :-)

The problem is that includes accepts an optional second parameter, which is the index at which to start searching, and some provides three arguments to its callback: The item, its index, and the object being searched.

So with

[0, 1].some(Array.prototype.includes, [1])

These calls are made (effectively):

  • [1].includes(0, 0) - false, the array doesn't contain 0
  • [1].includes(1, 1) - false, the array contains 1 at index 0, but the search starts at index 1

This comes up periodically. For instance, when trying to use parseInt as a callback directly to convert an array of strings into an array of numbers, because of parseInt's second parameter (the number base, or radix, to use):

console.log(["6", "9", "7"].map(parseInt));

How do I check if an array includes a value in JavaScript?

Modern browsers have Array#includes, which does exactly that and is widely supported by everyone except IE:

console.log(['joe', 'jane', 'mary'].includes('jane')); //true


Related Topics



Leave a reply



Submit