В javascript, как бы вы создали метод, сравнивающий значение A со значением B

У меня есть массив объектов, примерно так:

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 },
];

И я хочу получить определенные элементы, соответствующие определенным критериям. Например, человек, чье имя содержит число 5, а идентификатор - 435458. Или человек, у которого есть попугай или коза, или и то, и другое.

Метод, который я пытаюсь создать, принимает два аргумента: значение A и значение B. Значение A - это объект, например { 'name' : '5' }, { 'name' : /5/ } или { 'name' : 5 }or {'has': 'goats'}, а значение B - это объект, с которым выполняется сопоставление, т.е. myArray.

Этот метод быстро становится довольно сложным, и я чувствую, что мой код не настолько эффективен и действенен, как мог бы.

Я думаю, что лучший способ добиться этого - перебрать переданные и найденные объекты и массивы (массив myArray, has) и вызвать его самостоятельно, пока не будет сравнено только два значения строки / числа / регулярного выражения. Но я не совсем уверен, как лучше всего этого добиться. Или это не лучший выход? Также скорость - важный критерий успеха.

Ваше здоровье

Изменить: http://jsbin.com/ediye/edit содержит функцию, которую я использую сейчас, и я думаю, что он работает, как описано выше, но довольно медленно для больших наборов данных.


person Nicolas R    schedule 18.02.2009    source источник


Ответы (3)


Хорошо, я вижу, что довольно много людей интересуются этим, так что вот что у меня есть на данный момент:

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);
};

Что позволяет делать следующее:

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)

Это часть какой-то библиотеки js, над которой я работал, поэтому, если вы обнаружите, что работаете с json + dom, попробуйте выполнить поиск в коде Google для jdatastore.

person Nicolas R    schedule 27.02.2009

Вам просто нужно отфильтровать массив.

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

К сожалению, используется метод фильтрации JS 1.6, который пока не поддерживается в IE. Однако почти во всех фреймворках есть какой-то механизм фильтрации, использующий похожую идею. Если вы не хотите использовать фреймворк, вот решение от Mozilla's сайт:

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;
  };
}
person Rakesh Pai    schedule 18.02.2009
comment
Благодарю за ваш ответ. К сожалению, описанный вами метод фильтрации, хотя и полезен в определенных случаях, мне не нужен. Контекст, в котором я хочу достичь того, что я описываю, - это когда, например, у меня есть данные JSON, и я хочу, чтобы пользователь выполнял поиск в данных с помощью простого поля поиска. - person Nicolas R; 19.02.2009

Мне кажется, что то, что вы пытаетесь делать, идеально соответствует некоторому запросу к базе данных.

Для эффективности вам может быть лучше всего переместить этот код выбора на сервер, где вы можете использовать SQL-запрос.

person Himadri Choudhury    schedule 18.02.2009
comment
Да, это почти все, я хочу избежать использования серверной части и SQL, поскольку я имею дело с данными JSON, предоставленными третьими сторонами (см. Мой комментарий к ответу выше). До сих пор я делал следующее: jsbin.com/ediye Он работает нормально, но медленно при работе с много данных Ура - person Nicolas R; 19.02.2009
comment
Хм. Я вижу вашу дилемму по поводу использования серверной части. Я не уверен, что вы могли бы сделать, чтобы значительно повысить производительность вашего парсера javascript. - person Himadri Choudhury; 19.02.2009