2

This is a stupid question, it feels like one. But mental block is bad right now. :(

My problem is I have an array consisting only of numbers. I want to use that array as a lookup, but the number I pass to lookup a number in the array keeps looking to the array in the index of that number, not whether that number exists in the array.

For example:

var a = [2,4,6,8,10],
b = 2;

if(a[b]){ /* if the number 2 exists in the array a, then do something * }

But that looks at the array value in position 2 (6), not whether the value 2 is in the array. And this makes perfect sense, but (mental block) I can't figure out a way to test whether a number exists in an array of numbers... I even made everything strings, but it does type coercion and the problem persists.

Pulling my hair out here. Please help, thanks. :D

1

5 Answers 5

8
if (a.indexOf(2) >= 0)

Note that IE < 9 doesn't have indexOf, so you'll needto add it in case it doesn't exist:

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(searchElement /*, fromIndex */)
  {
    "use strict";

    if (this === void 0 || this === null)
      throw new TypeError();

    var t = Object(this);
    var len = t.length >>> 0;
    if (len === 0)
      return -1;

    var n = 0;
    if (arguments.length > 0)
    {
      n = Number(arguments[1]);
      if (n !== n) // shortcut for verifying if it's NaN
        n = 0;
      else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
    }

    if (n >= len)
      return -1;

    var k = n >= 0
          ? n
          : Math.max(len - Math.abs(n), 0);

    for (; k < len; k++)
    {
      if (k in t && t[k] === searchElement)
        return k;
    }
    return -1;
  };
}
Sign up to request clarification or add additional context in comments.

7 Comments

Snarky comment: How is this not using a loop?
You forgot to warn them that adding to Array.prototype breaks bad third party code (for ... in). @Tomalak he forgot to mention it's impossible without a loop
@Tom take a look at the fix. It uses a loop. indexOf also uses a loop internally. If you want O(1) lookup use an object.
@Raynos Well, using a loop in the JavaScript engine I'm ok with that, but in my code I want to avoid it. The JavaScript engines are obviously faster and stuffs. Object lookups are well known to be more expensive, too. Thus ruling that out.
@Tom "object lookups are well known to be more expensive" is a statement that is crying for something to back it up. Seriously, this is nonsense.
|
3

If you just want to check whether or not an item is contained by an Array, you can make usage of the nifty bitwise NOT operator along with the .indexOf() method:

if( ~a.indexOf(b) ) {
    // 2 was found, do domething here
}

It's always a good idea to use a shim or library to make sure methods like .indexOf() are available for your Javascript. @SLaks gave you the example for .indexOf() from MDC.

Short explanation why this works:

The bitwise not operator, negates all bits within a byte. That is also true for the positiv/negative bit. Basically it turns the result "-1" into "0". So, if nothing was found we have a falsy value which we want to have at this point. If we match something at the very beginning and get a result of "0" its translated into "-1" which is just fine since its not a falsy value. Any other possible return value is guaranteed a truthy value.

5 Comments

It is a nifty trick, and bit hackery is fun! :P Also, let's not forget, that the bitwise not turns positive to negative, too. So you end up with negative results.
@Tom: as I mentioned, that doesn't matter here. We get exactly what we desire. A truthy value if the element is contained by the Array and otherwise a falsy (0) value. Negative numbers are truthy in this language.
All very cool, but we have to remember that code that is 'too clever' can really just end up reducing readability and maintainability in any real software project. Really, why not just check for >= 0. Sure, maybe there are tiny/negligible performance advantages in some cases, but I don't know if this is one -- we still have at least two machine instructions to execute, bit-wise not and branch not zero. @jAndy is there a performance difference?
Thanks @jAndy. However, this shows that the performance differences are negligible, so then, what is the advantage? :)
@Peter: well, mozilla has a better performance (like 25%) on the bitwise operation. But in general, I think it may be also convinient to use that pattern just for "boolean"-like conditional. If don't care about the actual index. But I understand your point, to an unexperienced programer this can get pretty confusing and you might only want to use it in your own codebases if ever.
3

If you want a native look-up, use an object, not an array.

var a = {2:0,4:0,6:0,8:0,10:0},
    b = 2;

if (b in a) alert "yay!";

note that I use your array value as the key. Using 0 as the value is arbitrary, it does not matter what you put as the value when you use the in operator.

Use 1 or true if you want to be able to do

if (a[b]) alert "yay!";

but I'd recommend using in as this is both ideomatic and less error-prone.


EDIT: Regarding your notion that array lookup would be faster than object lookup. Try it.

console.log('begin building test array and object'); 
var x = [], y = {};
for (var i=0; i<1000; i++) {
  var n = Math.floor(Math.random() * 1000);
  x.push( n );
  y[n] = true;
}
console.log('finished building test array and object'); 

var foo = 0;

console.log('begin 1,000,000 array search rounds at ' + new Date());
for (var i=0; i<1000000; i++) {
  if (x.indexOf(i % 1000) > -1) foo++;
}
console.log('finished array search rounds at ' + new Date());


console.log('begin 1,000,000 object search rounds at ' + new Date());
for (var i=0; i<1000000; i++) {
  if ((i % 1000) in y) foo++;
}
console.log('finished object search rounds at ' + new Date());

7 Comments

Of course, but object lookups are more expensive. @Raynos I tried b in a, but that just returns true, which is no good if I want to get at the value.
@Tom did you just say object lookups are more expensive then for loops? That's wrong. (O(1) is not > O(N))
@Raynos If you want to return the value I mean. You can't say var c = b in a, and then say c === 2 ... As per speed, check it out here: jsperf.com/object-vs-array-perf
@Tom your benchmark is completely wrong : jsperf.com/array-loop-vs-object-lookup
@Tom 1) You are comparing apples and oranges here. The speed test you gave has nothing to do with the problem in your question. Indexing into an array to a known position is not the same as searching though it. 2) I'm using Chrome 14, the test results are absolutely the same for both objects and arrays, so your statement doesn't even hold true for the thing the test was designed for.
|
3

A simple loop seems called for here, but-

You can do it without a loop, and without extending IE, if you convert the array to a string.

var a = [2,4,6,8,10], n = 2;

if(RegExp('\\b'+n+'\\b').test(a.join(','))){
// n is in a
}

Comments

2

You can simply use .includes() method of arrays:

let a = [2, 4, 6, 8, 10],
    b = 2;

if(a.includes(b)) {
    // your code goes here...
}

1 Comment

Beautiful, so clean, so eloquent. Nice one!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.