在javascript数组中查找唯一对象的最快方法

时间:2015-07-07 13:38:26

标签: javascript arrays performance

我有一个大约15,000个javascript对象的数组。每个对象都有两个字段:

{
  name    : "Foo",
  address : "bar@moo.com"
}

我想创建一个只存储唯一电子邮件地址和相应名称的新数组。到目前为止,我有这种方法:

// temp1 is my array of 15,000 objects
var arr = [];

for (var i = 0; i<temp1.length; i++){
   var count = 0;
   if(!arr.length){arr.push(temp1[i])};
   for(var x = 0; x<arr.length; x++){
      if(temp1[i].address === arr[x].address){
        count++;
        if(temp1[i].name.length && !arr[x].name.length){arr[x] = temp1[i];} // Choose the new object if the old one has no name field
      }

      if((x === arr.length -1) && count === 0){
         arr.push(temp1[i])
      }
   }
}

我在这里有一个额外的要求 - 如果arr中的对象有一个空白字符串作为其名称字段,temp1对象,我想要存储而是temp1对象。

我目前的方法需要30秒才能在Chrome中运行,这并不理想。

编辑:为了澄清,我问Javascript中是否有更有效的方法来查找数组中的唯一对象。上面的一种方法是创建一个新数组,对原始数据进行迭代,并为每个循环遍历新数组中的所有内容以检查重复项。我想知道什么比这更有效。

4 个答案:

答案 0 :(得分:1)

这是另一种可能性

var tmp = {};

temp1.forEach(function(item) {
    var key = item.address;
    add = tmp[key] = tmp[key] || item;
    add.name = add.name || item.name;
});
var addr = Object.keys(tmp).map(function(t) { return tmp[t] });
警告:ie9或更高版本 - 或者对于较小的浏览器使用以下polyfill

map

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map forEach

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach Object.keys

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

考虑@ dev-null

的评论
var tmp = {}, item, key, add, i, l = temp1.length, addr;
for(i = 0; i < l; i++) {
    item = temp1[i];
    key = item.address;
    add = tmp[key] = tmp[key] || item;
    add.name = add.name || item.name;
};
addr = new Array(Object.keys(tmp).length);
i = 0;
for(key in tmp) {
    addr[i++] = tmp[key];
}

这平均速度是我第一次测试的两倍(尽管在Firefox中)

比OP的原始脚本快64倍

编辑:这是最快的(在Firefox中)

var tmp = {}, item, key, add, i, l = temp1.length, addr;
for(i = 0; i < l; i++) {
    item = temp1[i];
    key = item.address;
    add = tmp[key] = tmp[key] || item;
    add.name = add.name || item.name;
};
addr = Object.keys(tmp).map(function(t) { return tmp[t] });

答案 1 :(得分:0)

var seen = {},
    unique = arr.filter(function(item) {
        var address = item.address;
        return seen.hasOwnProperty(address ) ? false : (seen[address] = true);
    });

以同样的方式,您可以添加部件来存储temp1 thingy等。

答案 2 :(得分:0)

我会使用temp对象来存储address-&gt;名称对:

var tmp = {};

for ( var i = 0; i < temp1.length; i++ ) {
  var obj = temp1[i];
  if ( !tmp[obj.address] ) {
    tmp[obj.address] = obj.name;
  }
}

这会给你一个像这样的对象:

{
  "bar@moo.com": "Foo",
  "mail@example.com": "John Doe",
  ....
}

如果你想将它翻转回一个数组,你可能想要将完整的对象存储在tmp中:

var tmp = {};
for ( var i = 0; i < temp1.length; i++ ) {
  var obj = temp1[i];
  if ( !tmp[obj.address] || !tmp[obj.address].name ) {
    tmp[obj.address] = obj;
  }
}

这将产生这样的对象:

{
  "foo@bar.com": {
    "name": "John Doe",
    "address": "foo@bar.com"
  },
  ...
}

从对象回到数组非常简单:

var arr = [], i = 0;
for ( var prop in tmp ) {
  arr[i++] = tmp[prop];
};

如果使用new Array(length),可以进一步优化。因此,在初始过滤器迭代中,您需要计算新的长度。

答案 3 :(得分:0)

不是为循环中的每个新键检查arr,而是创建一个地图,然后在地图中查找。然后最后从地图创建对象。

http://jsbin.com/wunedaxabo/edit

var arr = [];
var hashMap = {};

var isEmptyName= function(hashMap, tempObject){

  var name = hashMap[tempObject.address];
  return name.trim().length === 0;

};

for (var i = 0; i<temp.length; i++){
   var count = 0;
   var tempObject = temp[i];
   //if not in the hashMap  or the entry has an emptyName push into the hasMap
   if(!hashMap[tempObject.address] || isEmptyName(hashMap, tempObject)){
     hashMap[tempObject.address] = tempObject.name;
   }   
}

for(var address in hashMap){
  arr.push({address: hashMap[address]});
}