PDA

View Full Version : Search Array for multiple values?



pram
Mar 18th, 2013, 09:18 PM
Hello all,

I'm new to this forum.

Is it possible to search an array for multiple values?

For example, I have an array that contains the following:


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");

The values can change or contain additional values.

Is there a method to loop through the array to check if it contains "Ford" or "Chevy"?


myCars.indexOf('Ford' || 'Chevy') > -1

Airblader
Mar 18th, 2013, 09:52 PM
What result do you expect? This will simply return a boolean value, but you can easily adapt it to return, for example, the index of the first occurence:


Array.prototype.containsOneOf = function (needles) {
for( var i = 0; i < needles.length; i++ ) {
if( this.indexOf( needles[i] ) > -1 ) {
return true;
}
}

return false;
}

Edit: Here's an example on how to use it:


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");

console.log( myCars.containsOneOf( ['Ford', 'Chevy'] ) ); // true
console.log( myCars.containsOneOf( ['Banana', 'Chevy'] ) ); // true
console.log( myCars.containsOneOf( ['Ford', 'Banana'] ) ); // true
console.log( myCars.containsOneOf( ['Foo', 'Bar'] ) ); // false

Old Pedant
Mar 18th, 2013, 10:03 PM
No built-in method, but it is so incredibly trivial to create your own that you shouldn't care.


function arraySearch( ary, find )
{
for ( var a = 0; a < ary.length; ++a )
{
for ( var f = 0; f < find.length; ++f )
{
if ( ary[a] == find[f] ) return ary[a];
}
}
return null;
}

Not the cleverest code in the world but pretty trivial to create. Only finds the first match. You pass it two arrays: The one to search and one that contains the things to find.

Or, if you are into regular expressions:


function arraySearch( ary, find )
{
if ( ! ( find instanceof Array ) ) { find = [ find ]; }
find = new RegExp( find.join("|"), "i" );
results = [];
for ( var a = 0; a < ary.length; ++a )
{
if ( find.test( ary[a] ) ) results.push( ary[a] );
}
return results;
}


This one is kind of fun, because you could call it via

arraySearch( myCars, ["Ford","Dodge"] ) // passing an array of what to find
arraySearch( myCars, "Ford" ) // passing a single string to find
arraySearch( myCars, "Ford|Buick" ) // passing an "or"ed regular expression

Old Pedant
Mar 18th, 2013, 10:07 PM
or, of course, convert either of those into Prototype methods, as Airblader did.


Array.prototype.containsAny = function( find )
{
if ( ! ( find instanceof Array ) ) { find = [ find ]; }
find = new RegExp( find.join("|"), "i" );
results = [];
for ( var a = 0; a < this.length; ++a )
{
if ( find.test( this[a] ) ) results.push( this[a] );
}
return results;
}

var myCars = ["Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep"];
document.write( myCars.containsAny( ["Ford","Dodge"] ) );
document.write("<hr>");
document.write( myCars.containsAny( "Ford|Buick" ) );


I think I like this regexp-based method for its flexibility.

Airblader
Mar 18th, 2013, 10:23 PM
Here are three functions, each one returning something different. None of them utilizes Old Pedant's RegEx version, though:


/**
* Returns true if at least one of the needles is contained.
*/
Array.prototype.containsOneOf = function (needles) {
if( !( needles instanceof Array ) ) {
needles = [needles];
}

for( var i = 0; i < needles.length; i++ ) {
if( this.indexOf( needles[i] ) > -1 ) {
return true;
}
}

return false;
}

/**
* Returns an array containing the index of the corresponding needles
*/
Array.prototype.indexOfMultiple = function (needles) {
if( !( needles instanceof Array ) ) {
needles = [needles];
}

var indices = [];
for( var i = 0; i < needles.length; i++ ) {
indices.push( this.indexOf( needles[i] ) );
}

return indices;
}

/**
* Returns an array consisting only of elements contained both in 'this' and needles.
*/
Array.prototype.filterContainedElements = function (needles) {
if( !( needles instanceof Array ) ) {
needles = [needles];
}

var contained = [];
for( var i = 0; i < needles.length; i++ ) {
if( this.indexOf( needles[i] ) > -1 ) {
contained.push( needles[i] );
}
}

return contained;
}

Old Pedant
Mar 18th, 2013, 10:46 PM
Heh! If he wasn't confused before, he will be now! Teach him not to ask for something specific.

rnd me
Mar 18th, 2013, 11:52 PM
It's simpler and faster to use built-in methods than to hand-code loopy utils...

the simplest way is to use RegExp instead of looping (which i try to avoid):


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");

myCars.some(/./.test, /Ford|Chevy/) // true if myCars contains "Chevy" or "Ford" (anywhere in array)



EDIT
you can easily add some safety to that using boundries (now that the simple method is demonstrated)


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");
myCars.some(/./.test, /\bFord\b|\bChevy\b/) // true if myCars contains "Chevy" or "Ford" (whole element match only)


and you can expand that to accept an array of other choices instead of a hand-coded regexp (still without a loop or custom function) :


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");
var myList=["Saab", "BMW", "Chevy"];

myCars.some(/./.test, new RegExp( "\\b("+myList.join("|")+")\\b" )); // true if myCars contains "Saab", "BMW", or "Chevy"(whole element match only)



bonus:

using the last syntax, it's super easy to make sure that EVERY item on a sub-list is on the main list (just in case anyone needs to know how to do that) :


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");
var myList=["Dodge", "Ford", "Chevy"];
myList.every(/./.test, new RegExp( "\\b("+myCars.join("|")+")\\b" )); // true



var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");
var myList=["Dodge", "Ford", "BMW"];
myList.every(/./.test, new RegExp( "\\b("+myCars.join("|")+")\\b" )); // false

Old Pedant
Mar 19th, 2013, 02:49 AM
Yes, I even considered doing it via a simple join on the array. And if all you want is a test for existence, any of those work fine. If you want to know *what* the matches are, though...

Of course, some and every don't exist in all browsers, but I know that doesn't bother you, which is fine. Just not something I will (yet) use. (And, yes, I know I could add them to the Array prototype, but then I'm back to doing a loop in JS code.)

rnd me
Mar 19th, 2013, 05:54 AM
Yes, I even considered doing it via a simple join on the array. And if all you want is a test for existence, any of those work fine. If you want to know *what* the matches are, though...


we can still use an array; "".match() is generic:


var myCars = new Array("Ford", "Chevy", "GMC", "Buick", "Dodge", "Jeep");

"".match.call( myCars, /Ford|Chevy/g )

this avoids the old-school IE problems associated with not having [].some() built-in...