views:

569

answers:

3

I have an array of objects, something like this:

var myArray = [
   { 'name' : 'some name', id: 23131, 'has' : ['dogs'] },
   { 'name' : 'some name 2', id: 8678, 'has' : ['dogs', 'cats'] },
   { 'name' : 'some name 3', id: 2125 , 'has' : ['donkeys', 'goats']},
   { 'name' : 'some name 4', id: 90867, 'has' : ['parrots', 'treasure'] },
   { 'name' : 'some name 5', id: 435458, 'has' : undefined },
];

And I want to retrieve specific elements that match certain criteria. For example, a person whose name contains number 5, and id is 435458. Or a person who has a parrot or a goat, or both.

The method I'm trying to build takes two arguments, value A and value B. Value A is an object, like { 'name' : '5' } or { 'name' : /5/ } or { 'name' : 5 }or { 'has' : 'goats' }, and value B is the object to match against, i.e. myArray.

The method is quickly becoming quite complex and I feel that my code is not quite as effective and efficient as it could.

I think the best way to achieve this is to loop through the objects and arrays that are passed and found (myArray, has array), and call it self until there only two string/number/regexp values to be compared against. But I'm not quite sure on how to best achieve this. Or is this not the best way to go? Also, speed is an important success criterion.

Cheers

Edit: http://jsbin.com/ediye/edit contains the function I'm using now, and I think it works as described above, but its quite slow for large data sets.

A: 

You simply need to filter the array.

var matchedItems = myArray.filter(function(item) {
    return (item.name.indexOf("5") !== -1) && (item.id === 435458);
});

Unfortunately, the filter method is JS 1.6 and is not supported in IE yet. However, almost all frameworks have some sort of filtering mechanism that uses a similar idea. If you don't want to use a framework, here's a solution from Mozilla's site:

if (!Array.prototype.filter)
{
  Array.prototype.filter = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var res = new Array();
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
      {
        var val = this[i]; // in case fun mutates this
        if (fun.call(thisp, val, i, this))
          res.push(val);
      }
    }

    return res;
  };
}
Rakesh Pai
Thank you for your response. Unfortunately the filter method you describe although useful in particular cases, is not what I am after.The context in which I want to achieve that I describe, is when for example I have JSON data and I want the user to search within data via a simple search box.
snz3
A: 

It seems to me that the kind of things you are trying to do map perfectly to some database query.

For efficiency it you might be best served to move this select code to the server where you can use a SQL query.

DasBoot
Yes that's pretty much it, I want to avoid using server side and SQL as I am dealing with JSON data provided from third parties (see my comment to above answer).What I did up to now is this: http://jsbin.com/ediyeIt works pretty ok but its slow when dealing with a lot of dataCheers
snz3
Hmmm. I see your dilema about using server side. I'm not sure what you could do to get a significant performance gain in your javascript parser.
DasBoot
+1  A: 

Ok, I'm seeing that quite a few people are interested in this so here's what I have up to now:

var filter = function(what,where) {
    var sameArrays = function(arr,arr1) {
     var al = arr.length, bl = arr1.length;
     if (al !== bl || al === 0 || bl === 0) { return; }
     for (var i = al - 1; i >= 0; i--){
      if (!match(arr[i], arr1[i])) { return; }
     }
     return arr1;
    };

    var sameObjects = function(obj,obj1) {
     for (var i in obj) {
      if(!obj1.hasOwnProperty(i)){return;}
      if (!match(obj[i],obj1[i])) {return;}
     }
     return obj1;
    };

    var inArray = function(value,obj) {
     var r = [],
      m = match;

     for (var i = obj.length - 1; i >= 0; i--){
      var item = obj[i];
      if( m(value,item) ) {
       r.push( item );
      }
     };

     return (r.length) ? r : undefined ;
    };

    var inObject = function(value,obj) {
     var r = [],
      m = match;

     for(var i in obj){
      var item = obj[i];
      if( m(value,item) ) {
       r.push( item );
      }
     };

     return (r.length) ? r : undefined ;
    };

    var inString = function(value,string) { 
     var valueConstructor = value.constructor;

     if( valueConstructor === RegExp) {
      return (value.test(string)) ? string : undefined ; }

     else if( valueConstructor === Function && value(string) === true ) {
      return string; }

    };

    var match = function(a,b) {
     if(typeof a === 'undefined' || typeof b === 'undefined' || a === null || b === null) {
      return; }

     if (a == b) { 
      return b;}

     var vc = a.constructor,
      oc = b.constructor;

    // Cannot compare array or object to a string or a number
     if( (vc === Array || vc === Object) && (oc === String || oc === Number) ) {
      return; }

     if( oc === Array && vc === Array ) {
      return sameArrays(a,b); }
     else if(oc === Array) {
      return inArray(a,b); }
     else if (oc === Object && vc === Object) {
      return sameObjects(a,b); }
     else if (oc === Object) {
      return inObject(a,b); }   
     else if (vc === Object || vc === Array) {
      return; }
     else if (oc === String || oc === Number){
      return inString(a,b); }

    };

    return match(what,where);
};

Which allows you do the following:

var b = [
   { 'name' : 'some name', age: 23, id: 0, 'has' : ['dogs'] },
   { 'name' : 'some name 2', age:24, id: 1, 'has' : ['dogs', 'cats'] },
   { 'name' : 'some name 3', age: 25, id: 2 , 'has' : ['donkeys', 'goats']},
   { 'name' : 'some name 4', age:26, id: 3, 'has' : ['parrots', 'treasure'] },
   { 'name' : 'some name 5', age:27, id: 4, 'has' : undefined }
];

filter({ age:23 },b)

filter({ age:'24' },b)

filter({ name: /\d$/ },b)

filter('dogs',b)

filter({ has: 'goats' },b)

This is part of some sort of js library that I've been working on, so if you find working with json + dom try searching google code for jdatastore.

snz3