Есть ли лучший способ поиска массива JavaScript, чем использование jQuery?

Мне часто приходится искать массив javascript, содержащий объекты. Я хочу искать объект в массиве, который имеет свойство. Например, поиск массива объектов Person, для которых id/ключ человека === "ABC123"

Это можно сделать довольно легко, используя jQuery, используя метод $.each, и это то, на чем я остановился. Вы можете увидеть пример здесь в jsFiddle. http://jsfiddle.net/johnpapa/EJAFG/

Мне интересно, нашел ли кто-нибудь еще более быстрый и/или лучший способ сделать это?

var Person = function(code, name) {
    this.code = code;
    this.name = name;
};
var people = [
    new Person("ABC123", "Tooth Fairy"),
    new Person("DEF456", "Santa Claus"),
    new Person("PIR000", "Jack Sparrow"),
    new Person("XYZ987", "Easter Bunny")
    ];

var utils = {};
// Could create a utility function to do this
utils.inArray = function(searchFor, property) {
    var retVal = -1;
    $.each(this, function(index, item) {
        if (item.hasOwnProperty(property)) {
            if (item[property].toLowerCase() === searchFor.toLowerCase()) {
                retVal = index;
                return false;
            }
        }
    });
    return retVal;
};

// or we could create a function on the Array prototype indirectly
Array.prototype.inArray = utils.inArray;

// let use the prototype for now
var i = people.inArray("PIR000", "code");
$('#output').text(people[i].name);

Есть много вопросов, подобных этому, но мне еще предстоит увидеть одно решение, отличное от итерации (как, например, здесь).

Итак, вопрос... есть ли лучший способ?

Ответы

Ответ 1

$, каждый из них будет о O (n), я бы подумал. Любой простой цикл "для", который ломается, когда он находит применимый элемент, будет не более O (n), но в среднем будет меньше, если последние элементы массива не будут постоянно считаться соответствующими элементами. Array.filter - это метод, который работает, но не является родным для некоторых браузеров. Существуют чистые javascript-реализации метода Array.filter, если вы так хотели его использовать. Для браузеров, которые размещают его изначально, он, вероятно, будет выполняться быстрее, поскольку их реализация, вероятно, скомпилирована и запущена в собственном коде. Но метод фильтра всегда будет давать O (n), поскольку он "фильтрует" элементы массива в новый массив.

Я лично придерживался подхода for (int я = 0;...). Меньше накладных расходов на изменение сферы, вызывая другие функции, и вы можете легко "разбить" на согласованный элемент.

Я также хотел добавить, что вы можете использовать локальное хранилище баз данных (которое использует SqlLite), предоставляемое HTML 5. Это, очевидно, не поддерживается широко, но будет намного быстрее, чем любой другой подход к использованию JavaScript, учитывая большой набор данных. Вот ссылка, если вы хотите проверить:

http://blog.darkcrimson.com/2010/05/local-databases/

Вот несколько способов сделать это на стене:. Теоретически вы можете индексировать свои данные и быстро извлекать их с помощью этих указателей. Вместо хранения ваших данных в массиве javascript вы храните его в DOM и "индексируете" элементы, используя классы CSS, такие как "data-id-5". Это дает вам преимущество использования встроенного API-интерфейса селектора, встроенного в большинство основных браузеров. Вот пример:

DOM:

 <div id="datastuff" style="display:none">
     <span class="data-id-ABC123" data-person='{"code": "ABC123", "name": "Tooth Fairy"}'></span>
     <span class="data-id-DEF456" data-person='{"code": "DEF456", "name": "Santa Claus"}'></span>
     <span class="data-id-PIR000" data-person='{"code": "PIR000", "name": "Jack Sparrow"}'></span>
     <span class="data-id-XYZ987" data-person='{"code": "XYZ987", "name": "Easter Bunny"}'></span>
 </div>

Теперь мы можем использовать jQuery и запрос для него: Мы запросим ключ "ABC123":

var person = $(".data-id-ABC123").data("person");
console.log(person.name);//Tooth Fairy

Ответ 2

В общем случае вы не можете получить элементы из массива быстрее, чем O (n), если вы не знаете что-то о том, что хотите индексировать.

Например, если вы индексируете somethnig, что сопоставимо, вы можете отсортировать массив и выполнить двоичный поиск.

Если вы выполняете поиск по столбцу, а значения - int или строки, вы можете использовать простые объекты Javascript в качестве хеш-таблиц.

var people = [
    new Person("ABC123", "Tooth Fairy"),
    new Person("DEF456", "Santa Claus"),
    new Person("PIR000", "Jack Sparrow"),
    new Person("XYZ987", "Easter Bunny")
];

var people_table = {};
for(var i=0; i<people.length; i++){
    people_table[ people[i].id ] = people[i];
}

//fast search:
var someone = people_table['ABC123'];

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

Ответ 3

Это не отвечает на ваш вопрос "поиска" как таковой, но это может быть решение для вас. Вы можете создать специализированный класс PersonArray, который индексирует людей внутри него. Производительность с этим подходом - O (1), но она использует больше памяти.

var PersonArray = function(persons) {
    this.elements = {};
    var i;
    for (i=0; i < persons.length; i++) {
        this.elements[persons[i].code] = persons[i];
    }
};

PersonArray.prototype.fromCode = function(s) {
    return this.elements[s];   
};

var people = new PersonArray([
    new Person("ABC123", "Tooth Fairy"),
    new Person("DEF456", "Santa Claus"),
    new Person("PIR000", "Jack Sparrow"),
    new Person("XYZ987", "Easter Bunny")
    ]);

console.log(people.fromCode("ABC123"));  // Prints a person
console.log(people.fromCode("DEF456"));  // Prints a person
console.log(people.fromCode("NONE"));  // Undefined

Вы можете расширить этот подход и индексировать другие поля.

Также смотрите: демо и эталонный тест ( с 100 000 элементов).

Ответ 4

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

Он хранит индекс имен объектов (если они есть), так что получение их является эффективным.

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

function Storage() {
  this.store = [];
  this.nameIndex = {};
}

// Add item to the store, if has name property, add name to name index
Storage.prototype.addItem = function(item) {
  var idx = this.nameIndex;

  // If the item has a name property
  if (item.hasOwnProperty('name')) {

    // If already have an item with that name, add index of
    // this item to indexs of same named items
    if (idx.hasOwnProperty(item.name)) {
      idx[item.name].push(this.store.length);

    // Otherwise, add this item to the index
    } else {
      idx[item.name] = [this.store.length];


    }
  }  
  // Add the item to the store
  this.store.push(item);
}

// Returns a possibly empty array of items with matching names
Storage.prototype.getItemsByName = function(name) {
  var result = [];
  var names;

  if (this.nameIndex.hasOwnProperty(name)) {
    list = this.nameIndex[name];

      for (var i=0, iLen=list.length; i<iLen; i++) {
        result.push(this.store[list[i]]);
      }
  }
  return result;
}

// Generic method for any property and value
Storage.prototype.getItemsByAttributeValue = function(propName, propValue) {
  // loop through items, return array of 
  // those with matching property and value
}


var store = new Storage();

store.addItem({name:'fred',age:'9'});

var obj = store.getItemsByName('fred');

alert(obj[0].age); // 9

store.addItem({name:'sally',age:'12'});

obj = store.getItemsByName('sally');

alert(obj[0].age); //12

Ответ 5

Может быть, вы можете закодировать его с помощью for..in. Смотрите: http://www.w3schools.com/js/js_loop_for_in.asp. Работает в стиле similair как php foreach.

Ответ 6

Если мне приходится много раз искать массив, я повторяю его один раз, в который я добавляю каждый ключ как свойство объекта, а затем просматриваю ключ в этом объекте. Это сохраняет цель всех поисков в O (n) + c. Хранение эффективно, поскольку объект хранит ссылки на данные массива, или они являются примитивами. Просто и быстро.