JavaScript: Как создать Объект и фильтр на тех атрибутах? [дубликат]

Способ сохранить отслеживание стека с помощью throw;, Это допустимо также

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex;, в основном похож на выдачу исключения от той точки, таким образом, отслеживание стека только пошло бы туда, где Вы выходите throw ex; оператор.

Mike также корректен, предполагая, что исключение позволяет Вам передавать исключение (который рекомендуется).

Karl Seguin имеет большая запись на обработке исключений в его основы программирования электронной книги также, который является большим чтением.

Редактирование: Рабочая ссылка на Основы Программирования PDF. Просто ищите текст "исключение".

10
задан Mr. Polywhirl 31 December 2014 в 15:12
поделиться

13 ответов

Javascript 1.6 (FF, Webkit-based) has built-in Array.filter function, so there's no need to reinvent the wheel.

result = homes.
  filter(function(p) { return p.price >= 150000 }).
  filter(function(p) { return p.price <= 400000 }).
  filter(function(p) { return p.bathrooms >= 2.5 }) etc

for a msie fallback see the page linked above.

15
ответ дан 3 December 2019 в 14:25
поделиться

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

Используйте прототип объекта для определения функция фильтра для объекта массива, которая принимает хэш параметров фильтров. Затем вы можете легко использовать этот хэш для создания и возврата нового массива домов, соответствующих желаемой собственности.

-1
ответ дан 3 December 2019 в 14:25
поделиться
o = ({
'homes' : 
[{
    "home_id"         : "1",
    "address"         : "321 Main St",
    "city"            : "Dallas",
    "state"           : "TX",
    "zip"             : "75201",
    "price"           : "20000",
    "sqft"            : "1100",
    "year_built"      : "2008",
    "account_type_id" : "2",
    "num_of_beds"     : "3",
    "num_of_baths"    : "3.0",
    "geolat"          : "32.779625",
    "geolng"          : "-96.786064",
    "photo_id"        : "14",
    "photo_url_dir"   : "\/home_photos\/thumbnail\/2009\/06\/10\/"
}
]})

Array.prototype.filterBy = function( by ) {
    outer: for ( 
    var i = this.length,
        ret = {},
        obj; 
        i--; 
    ) 
    {
    obj = this[i];
    for ( var prop in obj ) {

        if ( !(prop in by) ) continue

        if ( by[prop](obj[prop]) ) {
        ret[prop] = obj[prop]
        }

    }
    }

    return ret;
}

var result = o.homes.filterBy({
    price:function(price) {
        price = parseFloat(price)
        return price >= 15000 && price <=40000
    },
    num_of_baths:function(bathroom){
        bathroom = parseFloat(bathroom)
        return bathroom > 2.5
    },
    num_of_beds:function(bedroom){
        bedroom = parseFloat(bedroom)
        return bedroom === 1 || bedroom === 3
    }
});

for ( var p in result ) alert(p + '=' + result[p])
0
ответ дан 3 December 2019 в 14:25
поделиться

The Underscore.js library works well for these tasks. It uses native JavaScript commands when they are present. I've managed to get of many iterative loops using this library.

Bonus, the latest version is chainable, like jQuery.

1
ответ дан 3 December 2019 в 14:25
поделиться
Array.prototype.select = function(filter) {

    if (!filter) return this;

    var result = [], item = null;
    for (var i = 0; i < this.length; i++) {
        item = this[i];
        if (filter(item)) {
            result.push(item);
        }
    }

    return result;
}

function filterHomes(homes) {
    var a = 1, b = 2, c = 3, x = 4, y = 5, z = 6;
    return homes.select(function(item) {
        return between(item.price, x, y) && item.num_of_baths >= z && inArray(item.num_of_beds, [a, b, c]);
    });
}

function between(value, min, max) {
    return value >= min && value <= max;
}

function inArray(value, values) {
    for (var i = 0; i < values.length; i++) {
        if (value === values[i]) return true;
    }
    return false;
}
2
ответ дан 3 December 2019 в 14:25
поделиться

Я несколько раз пересматривал этот ответ, и люди могут посмотреть в вики, если есть интерес к изменениям, но это окончательное решение, которое я придумал. Он очень похож на многие другие, опубликованные здесь, главное отличие в том, что я расширил массив . prototype обернул функцию Array.prototype.filter таким образом, что тестовые операторы оцениваются в области действия элемента массива, а не массива.

Основное преимущество, которое я вижу в этом решении, а не в простое использование метода фильтра напрямую означает, что он позволяет вам писать общие тесты, которые проверяют значение this [key] . Затем вы можете создать общие элементы формы, связанные с конкретными тестами, и использовать метаданные из объектов, которые нужно отфильтровать (в данном случае дома), чтобы связать определенные экземпляры элементов формы с ключами. Это не только делает код более пригодным для повторного использования, но и, как мне кажется, упрощает создание запросов программным путем. Кроме того, поскольку вы можете определять общие элементы формы (например, множественный выбор для различных значений, или набор раскрывающихся списков для диапазона), вы также можете создать более мощный интерфейс запросов, в котором можно было бы динамически вводить дополнительные элементы формы, позволяя пользователям создавать сложные и настраиваемые запросы. Вот что разбивается на код:

Прежде чем что-либо делать, вы должны сначала проверить, существует ли функция filter , и расширить Array.prototype , если это не так. упоминается в других решениях. Затем расширите Array.prototype , чтобы обернуть функцию filter . Я сделал так, чтобы аргументы были необязательными на тот случай, если кто-то захочет использовать тестовую функцию, которая по какой-то причине не принимает их. Я также попытался включить ошибки, чтобы помочь вам реализовать код:

Array.prototype.filterBy = function(testFunc, args)
{
    if(args == null || typeof args != 'object') args = [args];
    if(!testFunc || typeof testFunc != 'function') throw new TypeError('argument 0 must be defined and a function');
    return this.filter(function(elem)
        {
        return testFunc.apply(elem, args);
    });
};

Это требует тестовая функция и массив аргументов, и определяет встроенную функцию как обратный вызов для функции Array.prototype.filter , которая вызывает function.prototype.apply для выполнения тестовой функции в области проверяемого элемента массива с указанные аргументы. Затем вы можете написать набор таких общих функций тестирования, как эти:

testSuite = {

     range : function(key, min, max)
     {
          var min = parseFloat(min);
          var max = parseFloat(max);
          var keyVal = parseFloat(this[key]);
          if(!min || !max|| !keyVal) return false;
          else return keyVal >= min && keyVal <= max;
     },

     distinct : function(key, values)
     {
          if(typeof key != 'string') throw new TypeError('key must be a string');
          if(typeof values != 'object') values = [values];
          var keyVal = this[key];
          if(keyVal == undefined) return false;


          for(var i in values)
          {
             var value = values[i];
             if(typeof value == 'function') continue;
             if(typeof value == 'string')
             {
                  if(keyVal.toString().toLowerCase() == value.toLowerCase()) return true;
                  else continue; 
             }
             else
             {
                 keyVal = parseFloat(keyVal);
                 value = parseFloat(value);
                 if(keyVal&&value&&(keyVal==value)) return true;
                 else continue;

            }
          }
          return false;
     }
};

Это просто тестовые функции, связанные с требованиями в вашем вопросе, однако вы можете создать дополнительные, которые будут делать более сложные вещи, например, проверять строковые значения по сравнению с регулярным выражением, или проверить значения ключей, которые не являются простыми типами данных.

Затем вы расширяете объект, содержащий массив домов, следующим образом (я назвал его homesData, назовите его как угодно в вашем коде):

 housesData.filterBy = function(tests)
 {
      ret = this.homes.slice(0);
      if(tests)
      {
           for(var i in tests)
           {
               var test = tests[i];
               if(typeof test != 'object') continue;
               if(!test.func || typeof test.func != 'function') throw new TypeError('argument 0 must be an array or object containing test objects, each with a key "func" of type function');
               else ret = ret.filterBy(test.func, test.args ? test.args : []);
           }
      }
      return ret;
 }

Затем вы можете вызвать эта функция выглядит так, чтобы получить набор результатов с использованием общих функций, определенных выше:

 result = housesData.filterBy([{func:range,args:['price','150000','400000'],
                      {func:distinct,args:['num_of_bedsf',[1, 2, 3]]}]);

На самом деле я бы хотел использовать это для сериализации общих элементов формы, о которых я упоминал ранее, в массив или хэш-карту тестовых объектов. Вот простой пример, который я использовал для тестирования этого кода (я использовал jQuery, потому что это проще, весь предыдущий код находится в example.js вместе с массивом фиктивных домов @ artlung):

 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html 
      PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:myNS="http://uri.for.your.schema" xml:lang="en" lang="en">
<head>
    <script src="jquery-1.3.2.js" type="text/javascript" language="javascript"></script>
    <script src="example.js" type="text/javascript" language="javascript"></script>
    <script type="text/javascript" language="javascript">
        runQuery = function(event)
        {
            var tests = [];
            $('#results').attr('value', '');;
            controls = $('#theForm > fieldset');
            for(var i = 0; i < controls.length ; i++)
            {
                var func;
                var args = [];
                control = controls.eq(i);
                func = testSuite[control.attr('myNS:type')];
                args.push(control.attr('myNS:key'));
                var inputs = $('input', control);
                for(var j=0; j< inputs.length; j++)
                {
                    args.push(inputs[j].value);
                }
                tests.push({func:func,args:args});
            }
            result = housesData.filterBy(tests);
            resultStr = '';
            for(var i = 0; i < result.length; i++)
            {
                resultStr += result[i]['home_id'];
                if(i < (result.length -1)) resultStr += ', ';
            }
            $('#results').attr('value', resultStr);
        }
    </script>
</head>
<body>
  <form id="theForm" action="javascript:null(0)">
    <fieldset myNS:type="range" myNS:key="price">
        <legend>Price:</legend>
        min: <input type="text" myNS:type="min"></input>
        max: <input type="text" myNS:type="max"></input>
    </fieldset>
    <fieldset myNS:type="distinct" myNS:key="num_of_beds">
        <legend>Bedrooms</legend>
        bedrooms: <input type="text" myNS:type="value"></input>
    </fieldset>
    <button onclick="runQuery(event);">Submit</button>
  </form>
  <textarea id="results"></textarea>
    </body>
 </html>
1
ответ дан 3 December 2019 в 14:25
поделиться

Мне нравится этот вопрос, поэтому я попробую. Как насчет «цепочечного» стиля кода, когда имеющийся у вас объект возвращает сам себя, как некоторые из фреймворков DOM JavaScript.

Я вызываю ваш объект MyObj :

MyObj.filter('home.price >= 150000')
     .filter('home.price <= 400000')
     .filter('home.num_of_baths >= 2.5')
     .filter('home.num_of_beds == 1 || home.bedrooms == 3');

И вот исходный код, этот пример работает.

var MyObj = {
    filter : function(rule_expression) {
        var tmpHomes = [];
        var home = {};
        for(var i=0;i<this.homes.length;i++) {
            home = this.homes[i];
            if (eval(rule_expression)) {
                tmpHomes.push(home);
            }
        }
        this.homes = tmpHomes;
        return this;
    },
    homes: [
    {
        "home_id"         : 1,
        "address"         : "321 Main St",
        "city"            : "Dallas",
        "state"           : "TX",
        "zip"             : "75201",
        "price"           : 300000,
        "sqft"            : 1100,
        "year_built"      : 2008,
        "account_type_id" : 2,
        "num_of_beds"     : 1,
        "num_of_baths"    : 2.5,
        "geolat"          : 32.779625,
        "geolng"          : -96.786064,
        "photo_id"        : "14",
        "photo_url_dir"   : "\/home_photos\/thumbnail\/2009\/06\/10\/foo.jpg"
    },
    {
        "home_id"         : 2,
        "address"         : "555 Hello World Way",
        "city"            : "Dallas",
        "state"           : "TX",
        "zip"             : "75201",
        "price"           : 200000,
        "sqft"            : 900,
        "year_built"      : 1999,
        "account_type_id" : 2,
        "num_of_beds"     : 1,
        "num_of_baths"    : 1.0,
        "geolat"          : 32.779625,
        "geolng"          : -96.786064,
        "photo_id"        : "14",
        "photo_url_dir"   : "\/home_photos\/thumbnail\/2009\/06\/10\/foo.jpg"
    },
    {
        "home_id"         : 3,
        "address"         : "989 Foo St",
        "city"            : "Dallas",
        "state"           : "TX",
        "zip"             : "75201",
        "price"           : 80000,
        "sqft"            : 1100,
        "year_built"      : 2003,
        "account_type_id" : 2,
        "num_of_beds"     : 3,
        "num_of_baths"    : 3,
        "geolat"          : 32.779625,
        "geolng"          : -96.786064,
        "photo_id"        : "14",
        "photo_url_dir"   : "\/home_photos\/thumbnail\/2009\/06\/10\/foo.jpg"
    },
    {
        "home_id"         : 4,
        "address"         : "1560 Baz Rd",
        "city"            : "Dallas",
        "state"           : "TX",
        "zip"             : "75201",
        "price"           : 100000,
        "sqft"            : 1100,
        "year_built"      : 2008,
        "account_type_id" : 2,
        "num_of_beds"     : 3,
        "num_of_baths"    : 1.5,
        "geolat"          : 32.779625,
        "geolng"          : -96.786064,
        "photo_id"        : "14",
        "photo_url_dir"   : "\/home_photos\/thumbnail\/2009\/06\/10\/foo.jpg"
    }
    ]
};
2
ответ дан 3 December 2019 в 14:25
поделиться

Итак:

var filteredArray = filter(myBigObject.homes, {

    price: function(value) {
        value = parseFloat(value);
        return value >= 150000 && value <= 400000;
    },

    num_of_baths: function(value) {
        value = parseFloat(value);
        return value >= 2.5;
    },

    num_of_beds: function(value) {
        value = parseFloat(value);
        return value === 1 || value === 3;
    }

});

И функция filter :

function filter( array, filters ) {

    var ret = [],
        i = 0, l = array.length,
        filter;

    all: for ( ; i < l; ++i ) {

        for ( filter in filters ) {
            if ( !filters[filter](array[i][filter]) ) {
                continue all;
            }
        }

        ret[ret.length] = array[i];

    }

    return ret;

}
5
ответ дан 3 December 2019 в 14:25
поделиться

Обратите внимание на функцию $. Grep в библиотеке jQuery . Даже если вы не используете библиотеку, вы можете посмотреть на код и увидеть, как они выполнили задачу, которую вы пытаетесь выполнить.

РЕДАКТИРОВАТЬ: Вот код:

 function filter(houses, filters) // pass it filters object like the one in @J-P's answer
 {
      retArr = $.grep(houses, 
           function(house)
           {
                for(var filter in filters)
                {
                     function test = filters[filter];
                     if(!test(house)) return false;
                }
                return true;
           });

      return retArr;
 }
2
ответ дан 3 December 2019 в 14:25
поделиться

Взгляните на JSONPath http://code.google.com/p/jsonpath/

Что-то вроде jsonPath (json, "$ .. homes [? (@ .price <400000)] "). toJSONString () должно работать.

JAQL также выглядит интересным для фильтрации JSON. http://www.jaql.org/

5
ответ дан 3 December 2019 в 14:25
поделиться

Я согласен не изобретать колесо, а просто использовать собственный метод фильтра массива.

Определение функции, показанное на странице Mozilla, добавит фильтр , если он отсутствует , независимо от браузера, с помощью обнаружения объектов ( if (! Array.prototype.filter) )

Любой из методов, предлагающих eval и / или несколько для циклы являются медленными и потенциально опасными .

Вы этого не сказали, но я предполагаю, что значения будут поступать из пользовательского ввода (формы), поэтому я что-нибудь сделаю например:

var filtered_results = obj.homes
    .filter(function (home)
    {
        return parseInt(home.price) >= document.getElementsByName('price_gt')[0].value;
    })
    .filter(function (home)
    {
        return parseInt(home.price) <= document.getElementsByName('price_lt')[0].value;
    })
    .filter(function (home)
    {
        return parseFloat(home.num_of_baths) >= document.getElementsByName('baths_gt')[0].value;
    });

еще лучше, не перебирайте список N раз, а просто выполните:

var filtered_results = obj.homes.filter(function (home)
{
    return (
        parseInt(home.price) >= document.getElementsByName('price_gt')[0].value &&
        parseInt(home.price) <= document.getElementsByName('price_lt')[0].value &&
        parseFloat(home.num_of_baths) >= document.getElementsByName('baths_gt')[0].value
    );
});

Я знаю, что это не соответствует запрошенному синтаксису, но этот подход более быстрый и гибкий (без использования eval или жестко заданных значений ).

0
ответ дан 3 December 2019 в 14:25
поделиться

Райан Линч - Если вы используете строки для сравнений («сравнение операторов значений»), вы можете использовать простой JavaScript (значение сравнение операторов). Логическая негибкость такая же, только с меньшей скоростью и безопасностью (через eval ()).

Отсутствие гибкости в моем примере происходит из-за знания, какие поля (цена, ванны и т. Д.) И какие операторы нас интересуют , но в исходном запросе плаката были указаны конкретные поля и сравнения (цена Y и т. д.).

GeorgeG - если вам интересно,

0
ответ дан 3 December 2019 в 14:25
поделиться

Забавно, что большинство из этих ответов ничего не делают, кроме как более элегантно изложить итерацию по массиву, хотя Джорджу это даже не нужно.

Предполагая, что у вас огромный JSON, вам нужно что-то быстрое. Итерация - это не то. Вам нужен jOrder ( http://github.com/danstocker/jorder ), который выполняет поиск в таблице из 1000 строк в 100 раз быстрее, чем итеративная фильтрация. Чем больше таблица, тем выше коэффициент.

Вот что вы делаете.

Поскольку jOrder не может обрабатывать более одного фильтра неравенства за раз, вам придется делать это в три этапа, но это все равно будет на порядок быстрее.

Сначала создайте таблицу jOrder на основе ваших необработанных данных и добавьте к ней индекс цен:

var table = jOrder(data.homes)
    .index('price', ['price'], { ordered: true, grouped: true, type: jOrder.number });

Вы можете повторно использовать эту таблицу в будущих поисках. Теперь вы начинаете с получения результатов, отфильтрованных по цене, и помещаете их в другую таблицу jOrder, и, наконец, добавляете индекс ванной комнаты:

var price_filtered = jOrder(table.where([{ price: { lower: 150000, upper: 400000 } }]))
    .index('bathrooms', ['num_of_baths'], { ordered: true, grouped: true, type: jOrder.number });

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

var bath_filtered = jOrder(price_filtered.where([{ num_of_baths: { lower: 2.5 } }]))
    .index('bedrooms', ['num_of_beds'], { grouped: true });

Наконец, вы получите полностью отфильтрованный набор:

var filtered = jOrder(bath_filtered.where([{ num_of_beds: 1 }, { num_of_beds: 3 }]));

Конечно, вы можете обернуть все это в функцию, которая принимает три аргумента:

  • {price : {нижний: 150000, верхний: 400000}}
  • {num_of_baths: {нижний: 2,5}}
  • [{num_of_beds: 1}, {num_of_beds: 3}]

и возвращает отфильтрованный .

2
ответ дан 3 December 2019 в 14:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: