如何在JavaScript中高效合并两个对象数组并按属性过滤重复项?

时间:2015-07-01 15:42:22

标签: javascript arrays

我还没有掌握JavaScript,我希望做同样的事情(How to merge two arrays in Javascript and de-duplicate items)但是使用对象数组并根据唯一的ID属性过滤重复项。

我已经用这个问题的答案之一做了但效率很低。

这是我的解决方案:

var mezclaArreglo = function (array) {
  var a = array.concat();
  for(var i=0; i<a.length; ++i) {
    for(var j=i+1; j<a.length; ++j) {
      if(a[i].hid === a[j].hid)
        a.splice(j--, 1);
    }
  }

  return a;
};

var old = 
  [{ hid: 'MTQzNTc1OTcyMzk1ODI3OTMyMjI3NDcyNzc0Njg0NDI5',
     number: '1',
     payload: { style: 'WebView', type: 'type1' }},
   { hid: 'MTQzNTc1OTczMDA1MDgwMTcwNzg3NjM4MDUzMjk3OTk3',
     number: '2',
     payload: { style: 'WebView', type: 'type1' }},
   { hid: 'MTQzNTc1OTczNDMxNzQ1NDI2NzUwOTA0ODgwNDY0MDc3',
     number: '3',
     payload: { style: 'WebView', type: 'type1' }}
   ];

var newA =
  [{ hid: 'MTQzNTc1OTczNDMxNzQ1NDI2NzUwOTA0ODgwNDY0MDc3',
     number: '3',
     payload: { style: false, type: false }},
   { hid: 'MTQzNTc1OTc0NzcxNDM1MDUyMzA5MzQ4MjQ3OTQ1MzA5',
     number: '4',
     payload: { style: 'WebView', type: 'type1' }}
  ];

// RESULT ARRAY
  [{ hid: 'MTQzNTc1OTcyMzk1ODI3OTMyMjI3NDcyNzc0Njg0NDI5',
     number: '1',
     payload: { style: 'WebView', type: 'type1' }},
   { hid: 'MTQzNTc1OTczMDA1MDgwMTcwNzg3NjM4MDUzMjk3OTk3',
     number: '2',
     payload: { style: 'WebView', type: 'type1' }},
   { hid: 'MTQzNTc1OTczNDMxNzQ1NDI2NzUwOTA0ODgwNDY0MDc3',
     number: '3',
     payload: { style: 'WebView', type: 'type1' }},
   { hid: 'MTQzNTc1OTc0NzcxNDM1MDUyMzA5MzQ4MjQ3OTQ1MzA5',
     number: '4',
     payload: { style: 'WebView', type: 'type1' }}
  ];

我需要以最有效的方式从 new 数组中删除重复的对象,而不是从数组中删除。

也许解决方案是使用像这个答案中的过滤方法? (https://stackoverflow.com/a/23080662/4275425) 我该如何为我的问题实现这个?

4 个答案:

答案 0 :(得分:5)

当您使用 3n 复杂度时,您的解决方案复杂性

var mergeWithoutDouble = function(array1, array2) {
    var mapHuidElement = {};

    for(var i = 0; i < array1.length; i ++){
        if(!mapHuidElement[array1[i]['huid']]){
            mapHuidElement[array1[i]['huid']] = array1[i];
        }
    }

    for(var i = 0; i < array2.length; i ++){
        if(!mapHuidElement[array2[i]['huid']]){
            mapHuidElement[array2[i]['huid']] = array2[i];
        }
    }

    var arrayMerged = Object.keys(mapHuidElement).map(function (key) {return mapHuidElement[key]});

    return arrayMerged;
}

注意:您可以将huid作为参数,使其更具通用性,我认为我们也可以对其进行优化。

例如:

mergeWithoutDouble([{huid: 1}, {huid: 3}], [{huid: 2}, {huid: 3}]);

=&GT; [{huid: 1}, {huid: 2}, {huid: 3}]

编辑:将多个属性作为唯一键:如果我们要将多个属性用作唯一键。

var mergeWithoutDouble = function(array1, array2, uniqueKeys) {
    var mapHuidElement = {};

    for(var i = 0; i < Math.max( array1.length, array2.length ) ; i ++){
        var a = i < array1.length,
            b = i < array2.length,
            key;
        if(a){
            key = "";
            for(var k = 0; k < uniqueKeys.length; k++){
                key += uniqueKeys[k]+":"+array1[i][uniqueKeys[k]]+";";
            }
            if(!mapHuidElement[key]){
                mapHuidElement[key] = array1[i];
            }
        } 
        if(b){
            key = "";
            for(var k = 0; k < uniqueKeys.length; k++){
                key += uniqueKeys[k]+":"+array2[i][uniqueKeys[k]]+";";
            }
            if(!mapHuidElement[key]){
                mapHuidElement[key] = array2[i];
            }
        }
    }

    return Object.keys(mapHuidElement).map(function (key) {return mapHuidElement[key]});
}

示例:

mergeWithoutDouble([{huid: 1, name: 'A'}, {huid: 1, name: 'B'}, {huid: 3, name:'A'}], [{huid: 2, name: 'A'}, {huid: 3, name: 'A'}], ['huid', 'name']);

=&GT; [{huid: 1, name: 'A'}, {huid: 1, name: 'B'}, {huid: 3, name:'A'}, {huid: 2, name: 'A'}]

精简版:

var mergeWithoutDouble=function(t,e,r){for(var n={},a=0;a<Math.max(t.length,e.length);a++){var h,f=a<t.length,g=a<e.length;if(f){h="";for(var l=0;l<r.length;l++)h+=r[l]+":"+t[a][r[l]]+";";n[h]||(n[h]=t[a])}if(g){h="";for(var l=0;l<r.length;l++)h+=r[l]+":"+e[a][r[l]]+";";n[h]||(n[h]=e[a])}}return Object.keys(n).map(function(t){return n[t]})};

我们现在看一下表演:

2个10 000个元素的数组,10个重复(1个键:huid)。

  

在我的机器上
  versionAntoinev1:15.000ms
  versionAntoinev2:54.000ms
  versionVladimir:1749.000ms

这就是避免n²复杂性的重要原因。

Plunker

答案 1 :(得分:1)

根据@Paolo Moretti的评论

更新

查看underscore.js

一个简单有效的解决方案是使用 _ .uniq 这样的

_.uniq(data1.concat(data2), false, 'hid')

请参阅此DEMO

答案 2 :(得分:1)

首先,我喜欢这样的问题所以感谢您的提问。我喜欢将此视为哈希冲突问题(其中hid是“哈希”)。你可以创建一个如下所示的函数:

function mergeWithoutDups() {
  var merged = [],
    map = {};
  for(var i = 0; i < arguments.length; i++) {
    for(var j = 0; j < array.length; j++) {
      var item = array[j];
      var hid = item.hid;

      if(!map[hid]) {
        map[hid] = true;
        merged.push(item);
      }
    }
  }

  return merged;
}

基本上它的作用是使唯一标识符成为任意map对象的键,并让我们在内部知道具有相同hid的对象已放置在数组中。如果尚未放置具有特定hid的对象,则将其推入生成的数组中,并更新map对象以反映更改。此函数还允许将任意数量的数组传递给它:

mergeWithoutDups(ar1, ar2, ar3, ar4, ...)

结果是一个数组,其中维护订单,其中的项目由hid唯一。此外,它应该是高性能的,因为所有数组都只迭代一次。注意:如果您希望较新的项目优先于较旧的项目,则应该使用位于参数列表前面的较新数组来调用它。 e.g。

mergeWithoutDups(newArray, oldArray)

关于@ Stoyan的回应,Underscore.js受到广泛支持,并且可能具有相当优化的uniq功能。如果您的项目允许,我建议使用它。

干杯!

答案 3 :(得分:0)

function merge(o, n) {
  return n.concat(o).filter(function(x, i, all) {
    return !all.some(function(y, j) {
      return x.hid == y.hid && j > i;
    });
  });
}

var old = [{
  hid: 'MTQzNTc1OTcyMzk1ODI3OTMyMjI3NDcyNzc0Njg0NDI5',
  number: '1',
  payload: {
    style: 'WebView',
    type: 'type1'
  }
}, {
  hid: 'MTQzNTc1OTczMDA1MDgwMTcwNzg3NjM4MDUzMjk3OTk3',
  number: '2',
  payload: {
    style: 'WebView',
    type: 'type1'
  }
}, {
  hid: 'MTQzNTc1OTczNDMxNzQ1NDI2NzUwOTA0ODgwNDY0MDc3',
  number: '3',
  payload: {
    style: 'WebView',
    type: 'type1'
  }
}];

var ne = [{
  hid: 'MTQzNTc1OTczNDMxNzQ1NDI2NzUwOTA0ODgwNDY0MDc3',
  number: '3',
  payload: {
    style: false,
    type: false
  }
}, {
  hid: 'MTQzNTc1OTc0NzcxNDM1MDUyMzA5MzQ4MjQ3OTQ1MzA5',
  number: '4',
  payload: {
    style: 'WebView',
    type: 'type1'
  }

}];

document.write('<pre>' + JSON.stringify(merge(ne, old), null, '\t') + '</pre>');