我经常需要搜索包含对象的javascript数组。我想在数组中搜索具有属性匹配的对象。例如,在Person对象的数组中搜索person的id / key ===“ABC123”
使用$ .each方法使用jQuery可以很容易地完成它,这是我所确定的。你可以在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's use the prototype for now
var i = people.inArray("PIR000", "code");
$('#output').text(people[i].name);
有很多问题与此类似,但我还没有看到一个解决方案而不是迭代(就像我在这里做的那样)。
所以问题是......有更好的方法吗?
答案 0 :(得分:16)
$。每个人都会想到O(n)。任何简单的“for”循环在找到适用的项目时会断开,最多只能是O(n),但平均来说会更少,除非数组的后一项经常被发现是匹配元素。 Array.filter是一种有效的方法,但不是某些浏览器的原生方法。如果你希望使用它,那么Array.filter方法有纯粹的javascript实现。对于本机托管它的浏览器,它可能执行得更快,因为它们的实现可能是在本机代码中编译和运行的。但是过滤器方法总是会产生O(n),因为它将数组的元素“过滤”成一个新数组。
我个人坚持使用for(int i = 0; ...)方法。通过调用其他函数可以减少范围更改的开销,并且可以轻松地“中断”匹配的元素。
我还想补充一点,你可以使用HTML 5提供的本地数据库存储(使用SqlLite)。这显然不受广泛支持,但是比给定足够大的数据集的任何其他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
答案 1 :(得分:7)
一般情况下,你不能比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中轻松完成,因此发送处理服务器端可能是个好主意,这样您就可以使用更合适的工具,例如关系数据库。
答案 2 :(得分:6)
这本身并不能解答您的“搜索”问题,但它可能是您的解决方案。您可以创建一个专门的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
您也可以扩展此方法以索引其他字段。
另请参阅:a demo和a benchmark(包含100,000个元素)。
答案 3 :(得分:1)
如果您打算这么做,那么您可能希望为特定属性创建索引,以便可以更快地返回项目。例如下面实现了一个存储对象,用于添加和获取添加到其中的对象。
它保存对象名称的索引(如果它们有一个),以便获得它们是有效的。
你只会注意到大量对象的性能提升(比如超过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
答案 4 :(得分:0)
也许你可以用for..in循环它。见:http://www.w3schools.com/js/js_loop_for_in.asp。像php的foreach一样以类似的方式工作。
答案 5 :(得分:0)
如果我必须重复搜索数组,那么我迭代一次,在其中我将每个键添加为对象的属性,然后在该对象中查找键。这使所有查找的目标保持在O(n)+ c。存储是有效的,因为对象存储对数组数据的引用,或者它们是基元。简单快捷。