A hopefully faster bidirectional indexOf
/ lastIndexOf
alternative
2015
While the new method includes
is very nice, the support is basically zero for now.
It's a long time that I was thinking of a way to replace the slow indexOf
/lastIndexOf
functions.
A performant way has already been found, looking at the top answers. From those I chose the contains
function posted by @Damir Zekic which should be the fastest one. But it also states that the benchmarks are from 2008 and so are outdated.
I also prefer while
over for
, but for not a specific reason I ended writing the function with a for loop. It could be also done with a while --
.
I was curious if the iteration was much slower if I check both sides of the array while doing it. Apparently no, and so this function is around two times faster than the top voted ones. Obviously it's also faster than the native one. This is in a real world environment, where you never know if the value you are searching is at the beginning or at the end of the array.
When you know you just pushed an array with a value, using lastIndexOf remains probably the best solution, but if you have to travel through big arrays and the result could be everywhere, this could be a solid solution to make things faster.
Bidirectional indexOf
/lastIndexOf
function bidirectionalIndexOf(a, b, c, d, e){
for(c=a.length,d=c*1; c--; ){
if(a[c]==b) return c; //or this[c]===b
if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
}
return -1
}
//Usage
bidirectionalIndexOf(array,'value');
Performance test
https://jsbench.me/7el1b8dj80
As a test I created an array with 100k entries.
Three queries: at the beginning, in the middle & at the end of the array.
I hope you also find this interesting and test the performance.
Note: As you can see I slightly modified the contains
function to reflect the indexOf
& lastIndexOf
output (so basically true
with the index
and false
with -1
). That shouldn't harm it.
The array prototype variant
Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
for(c=this.length,d=c*1; c--; ){
if(this[c]==b) return c; //or this[c]===b
if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
}
return -1
},writable:false, enumerable:false});
// Usage
array.bidirectionalIndexOf('value');
The function can also be easily modified to return true or false or even the object, string or whatever it is.
And here is the while
variant:
function bidirectionalIndexOf(a, b, c, d){
c=a.length; d=c-1;
while(c--){
if(b===a[c]) return c;
if(b===a[d-c]) return d-c;
}
return c
}
// Usage
bidirectionalIndexOf(array,'value');
How is this possible?
I think that the simple calculation to get the reflected index in an array is so simple that it's two times faster than doing an actual loop iteration.
Here is a complex example doing three checks per iteration, but this is only possible with a longer calculation which causes the slowdown of the code.
https://web.archive.org/web/20151019160219/http://jsperf.com/bidirectionalindexof/2
~[1,2,3].indexOf(4)
will return 0 which will evaluate as false, whereas~[1,2,3].indexOf(3)
will return -3 which will evaluate as true.~
is not what you want to use to convert to a boolean, for that you need!
. But in this case you want to check equality with -1, s o the function might endreturn [1,2,3].indexOf(3) === -1;
~
is a binary not, it will invert each bit of the value individually.[1,2,3].indexOf(4)
will actually return -1. As @mcfedr pointed out,~
is the bitwise-NOT operator, see ES5 11.4.8. Thing is, since the binary representation of-1
consists of only 1's, it's complement is0
, which evaluates as false. The complement of any other number will be non-zero, hence true. So,~
works just fine and is often used in conjunction withindexOf
.[[1,2],[3,4]].includes([3,4])
?