Tuesday, September 25, 2007

[TOD] The most compact Array.prototype.indexOf

The TOD is a JavaScript standard Array indexOf prototype implementation.

I know this is a basic trick but many times useful in IE too:

Array.prototype.indexOf=function(o,i){for(var j=this.length,i=i<0?i+j<0?0:i+j:i||0;i<j&&this[i]!==o;i++);return j<=i?-1:i}

That's all ... in the most compact way ... but what about most compact lastIndexOf prototype?

Array.prototype.lastIndexOf=function(o,i){var s=this,r=s.reverse().indexOf(o,i);s.reverse();return r}


Update
Thanks to Laurent, the most compact lastIndexOf should be this one:

Array.prototype.lastIndexOf=function(o,i){return this.slice(0).reverse().indexOf(o,i)}

Probably one slice instead of two reverse should be a better choice? I'll test them :-)

Performances? Good enough ... Compatibility? IE4 or greater!

11 comments:

Laurent said...

These are shorter and imho a little cleaner.

indexOf:
function(a){return this.reverse().lastIndexOf(a);}
lastIndexOf:
function(a){for(var i=this.length-1; i>-1 && a!==this[i]; --i); return i;}

Andrea Giammarchi said...

These are wrong, because not standard and indexOf will reverse every Array.


var a = [1,2,3];
a.reverse();
alert(a); // [3,2,1]

reverse return an array but reverse internal indexes too ... since JavaScript 1.0.

Regards

Laurent said...

Ah right, so a quick fix for indexOf would go something like:
indexOf:
function(a){var i=this.reverse().lastIndexOf(a); this.reverse(); return i;}

I don't see how the reverse internal indices affect the js however, since I'm using an external counter, so to speak.

Laurent said...

Actually it would be better to do something like this for indexOf:

function(a){return this.slice(0).reverse().lastIndexOf(a);}

Andrea Giammarchi said...

Laurent, your last proposal seems interesting but it's not standard.

Please read correct indexOf and lastIndexOf implementation:
JavaScript MDC

At the same time, slice create a copy and I don't know how much this should increase memory or CPU usage.

Laurent said...

You can also remove 1 byte by rewriting the return statement in the indexOf function as follows:
return i<j?i:-1

Anonymous said...

Sorry this is late, but I only just saw this.

You can save a couple more characters by making 'j' a parameter instead of using 'var':

function(o,i,j){for(j=...

And I think you can use the binary operator '&' instead of '&&'. Not sure if that's portable though.

It will also sometimes do an out of bounds array access, and an extra comparison but I think it's still correct.

Andrea Giammarchi said...

You can save a couple more characters by making 'j' a parameter instead of using 'var'

That's not standard ... and a dynamic language need to do more check each time You call function (was third parameter sent? what' its real value?)

& intead of && is not the same ... so, again, You found here the shortest Array.prototype.indexOf :-)

DBJ said...

function dbjIndexOf( elem, array )
{
var i = array.length ;
do {
if ( i-- === 0 ) return i ;
} while ( array[i] !== elem ) ;
return i ;
}

Andrea Giammarchi said...

DBJ your indexOf does NOT respect SPECs

Andrea Giammarchi said...

if you don't need to follow specs, this is the shortest:

function indexOf(v){
for(var i=this.length;i--&&this[i]!==v;);
return i
};