Testing for a Value in JavaScript Array
Ever have an IF statement that has a whole bunch of OR (||) statements that keep testing a variable against a bunch of values?
if(name == 'bobby' || name == 'sue' || name == 'smith') { ... }
In JavaScript, there's an in
operator that tests whether a property is in an object. We can actually use this to mimic the PHP function in_array
.
if(name in {'bobby':'', 'sue':'','smith':''}) { ... }
If the value of name
matches any of the keys in the object, it returns true. Now, I personally find that looks a little ugly since we have to assign values to each of our keys in order to get this to work. So, I made a quick function that simply converts an array into an object.
function oc(a)
{
var o = {};
for(var i=0;i<a.length;i++)
{
o[a[i]]='';
}
return o;
}
I called it oc
because it's short and stands for object converter. Using our function, we can now test for values in an array like so:
if( name in oc(['bobby', 'sue','smith']) ) { ... }
It's important to note that this is really only advantageous for doing string comparisons since values get converted to strings once in the array.
25 in oc(['25', '10','15']) // returns true
null in oc(['null']) // returns true
['test'] in oc(['test']) // returns false
['test'] in oc([['test']]) // returns true
['foo','bar'] in oc(['foo','bar']) // returns false
['foo','bar'] in oc([['foo','bar']]) // returns true
'foo,bar' in oc([['foo','bar']]) // returns true
{} in oc({}) // returns true
new Object() in oc({}) // returns true
new Object() in oc([{albert:''}]) // returns true
'[object Object]' in oc([{albert:'test'}]) // returns true
As I hope is evident from that stream of examples, objects get converted into strings and it is that string that is compared. In any case, for simpler comparisons, I like the readability of the in
operator and having people understand your code is sometimes half the battle.
Conversation
Sweet. I didn't know about In. At the moment I use
Array.prototype.has=function(v){
for (i=0;i<this.length;i++){
if (this[i]==v) return i;
}
return false;
}
which has the added benefit of returning the index of the value, but using it is ugly since you must use strict comparison to test it.
['foo','bar'].has('foo') // returns 0
But, it works for numeric values as well as strings.>
Sorry, didn't escape the less than. Should read
Array.prototype.has=function(v){
for (i=0; i<this.length; i++){
if (this[i]==v) return i;
}
return false;
}
I use regular expressions for this (when it comes to the case that an array is created solely for testing purposes). Creating an object literal seems like too much overhead for a test. If I used arrays at all, I would use Graham's method.
if (/^bobby|sue|smith$/.test(name))
{ .. }
Andy: huh, now why did I think .test() wouldn't work? And graham's solution is nice, although, I'd have it return true or false instead as it'd be nicer not to have to test for false in an if statement.
I suppose thats true, I forget about that zero-index sometimes.
You're right, although i originally intended the function as an index lookup rather than a containment test. Allowing both would probably be better
Array.prototype.has=function(v,i){
for (var j=0;j<this.length;j++){
if (this[j]==v) return (!i ? true : j);
}
return false;
}
['foo','bar'].has('foo') // returns true
['foo','bar'].has('foo',1) // returns 0
If you're adding extra arguments it might be a better idea to just separate them.
Array.prototype.indexOf = function(obj) {
for (var i = 0; i < this.length; i++) {
if (this[i] == obj)
return i;
}
return -1;
}
Array.prototype.has = function(obj) {
return this.indexOf(obj) >= 0;
}
I have been using a 'contains' method on
Array.prototype
for a while now, but i do like the idea of usingin
instead - the orthogonality with hashes is something i miss from python.I think you could improve
oc
by just usingarguments
as its source array instead ofa
to remove the syntactic kruft introduced by the array literal eg:allowing checks like
I woud use a combination of the method mentioned in the article and Andrews comment #8, so that the function could receive EITHER an array OR string arguments.
allows for check like
OR
This would be useful because you want to be able to pass the function a variable which contains an array.
I found out about this type of code from the following blog post: Sets in JavaScript last year some time. It's decent for small arrays, but for larger arrays, you're really much better off using a function that drops out the moment it finds the proper value rather iterate over every element in the array.
I generally use an indexInArray() function which returns the index of the element within the array, and a -1 if not found for those situations:
if(indexInArray(arr, val)>-1){
// found element
}
function indexInArray(arr,val){
for(var i=0;i<arr.length;i++) if(arr[i]==val) return i;
return -1;
}
All the proposed solutions involving arrays are not efficient at all.
The only efficient way to check for strings in a set is to use RegExp.
However Andy's proposed solution is wrong too as the RegExp is incorrectly bounded : it matches "bobbyblabla", "blasuebla" and "blablasmith" (and many more).
The solution is to group the strings to match between ^ and $.
Here is the fix:
I forgot a '/':
Iterating through arrays is an attempt to make the search process more generic, so that you're not tied to one construct for strings, and another for other value types. It all depends on what you're trying to accomplish.
The indexInArray function I displayed above could easily be modified to allow a comparison function to be passed in as well to allow for custom checks against a varitey of object types, not just strings. (off the top of my head):
function indexInArray(arr, val, compFN){
for(var i=0, len=arr.length;i<len;i++){
if(compFN(arr[i], val)){
// positive match
}
}
If you're going the RegExp route and you know tha the set of strings could be arbitrary, then you could generate the RegExp on the fly using the Array join() method.
Olivier, do you have any links that show how the RegExp processing is compared to Array iteration in JavaScript? It would be good to see the data behind that.
I ran some tests using all of these methods and the results are negligible even for extremely large lists. Its all up to personal preference. I think that RegExp is the cleanest of all the methods though, which is why I use it.
Code for RegExp of pre-assembled array:
Actually, I usually use an equivalent to the Array.has if I have a pre-assembled array. That RegExp version for a pre-assembled array is pretty ugly.
As the string to check are only ASCII, the hash version can be shortened like this (quotes removed, value '' replaced by 0):
But I maintain that I prefer the RegExp version in case of a constant set of strings (sorry, no numbers to show), because the RegExp object is built just once for the whole execution, at the script parsing time.
When the set is created dynamically at runtime (not known at parsing time), creating the RegExp may not be worth it for just one pass.For multiple passes the RegExp must be cached in a variable outside a loop to be used inside a loop.
Also, building a reliable RegExp is difficult as it requires to escape meta characters: relying on the Andy version is acceptable only if we are sure that the names are pure alpha-numeric.
This kind of problem has been often been studied in Perl mailing lists as Perl has the same data structures. Have a look to this entry in the Perl FAQ.
You should simply use indexOf or some ... that's are JS 1.6 Array methods *
Array.prototype.has = function(obj) {
return this.indexOf(obj) !== -1;
};
Array.prototype.has = function(obj) {
return this.some(function(e){return e === obj});
};
this implementation is for multiple search (more than one value that should be present)
Array.prototype.has = function() {
var i = arguments.length,
result = [];
while(i)
result.push(this.indexOf(arguments[--i]) !== -1);
return result.every(function(e){return e});
};
alert([1,2,3].has(3)); //true
alert([1,2,3].has(2,4)); // false
alert([1,2,3].has(1,2,3)); // true
* To have JS 1.6 with every browser (starting from IE4) just develop your code adding JSL at the top - http://www.devpro.it/JSL/
I'm pretty sure there's a good reason to avoid using the 'in' operator, and that's that IE5.01 doesn't completely understand it .
I suspect it has something to do with the lack of hasOwnProperty support too.
If I remember right, it throws a syntax error at parse time... but I could be wrong.
If you already have prototype.js loaded, try this one:
Set function match to test whatever it is you want. In this case its testing every element against the string 'b'.
This can be simplified down even more if you want.
if(name== ('bobby' ||'sue' ||'smith')) { ... }
You can simply check if an array has a key by using the 'in' operator
var arrTest = new Array();
arrTest["key"] = "value";
if("key" in arrTest){
// this code will run
}
Cool thx! This saves some extra for loops.
And I didn't even know you could use the "name in {}" format :)
I had never used "IN" for anything but enumeration. Thanks a lot for this post!
Hi all,
Am new to Javascript, I was just wondering if you have better solution for my comparison of string.
Comparison description:
* I would like to have a lesser comparison or the number of OR statements...
ex :
if ( name == 'fish' && fish.name == 'Goldfish' || fish.name == 'goldfish' || fish.name == 'GOLDFISH' ... )
{
//do something here!
}
I think of using the no case sensitive of the strings..but I just don't know how to implement this.
Hoping for good experts advice.
if you're just looking to make string comparison case insensitive, just convert to lowercase. For example, if fish.name.toLowerCase() == 'goldfish'. That'll save having to test every variation.
Just wanted to say thanks. This helped me a ton on a current project. I'm going to study the object literal a little more to get a better grasp on it but for now you saved me :)
I think indexOf method can be used for same trick with one line solution.
if (['bobby','sue','smith'].indexOf(name) >= 0) {
alert('I know that person');
} else {
alert("I can't remember, sorry!");
}