【问题标题】:Most efficient way to merge two arrays of objects合并两个对象数组的最有效方法
【发布时间】:2013-02-07 06:39:27
【问题描述】:

我已经解决了这个问题。但是,我正在寻找更快的解决方案,因为我的变量有数千个对象。

我有两个这样的数组:

var full = [{a:'aa1',b:'bb1'},{a:'aa3',b:'bb2'},{a:'aa3',b:'bb3'},{a:'aa2',b:'bb3'}],
some = [{a:'aa1',b:'bb1'},{a:'aa3',b:'bb3'}]; 

如果对象存在于某些对象上,我正在尝试在 full 中的一个名为 c 的新属性中标记。预期结果:

 [{a:'aa1',b:'bb1',c:true},{a:'aa3',b:'bb2'},{a:'aa3',b:'bb3',c:true},{a:'aa2',b:'bb3'}]

一些重要提示:

  • 一些元素的元素总是比完整元素少
  • 两个数组排序相等

我目前的做法是:

var getIndexByAB = function(arr, a,b){
     var initialIndex =  getIndexByAB.initialIndex || 0,
     len = arr.length;
     for(initialIndex; initialIndex < len ;initialIndex++ ){
         var el = arr[initialIndex];
         if( el.b === b && el.a === a ){
             getIndexByAB.initialIndex = initialIndex;
             return initialIndex;
         }
     }
     return -1;
}

var len = some.length;
for(var i = 0; i < len ; i++){
 var el=some[i],
 index = getIndexByAB(full,el.a,el.b);
 if(index > -1) full[index].c = true;
}

UPDADE:使用 Juan 评论改进了原始解决方案。

【问题讨论】:

    标签: javascript arrays performance algorithm merge


    【解决方案1】:

    由于数组都已排序并且some 严格小于full,因此您可以通过使用不同索引同时遍历两个数组来节省一些时间。事实上,你每次都在遍历full来获取匹配元素的索引,所以你有O(N^2)的运行时间,但你只需要从您匹配的最后一个元素。

    【讨论】:

    • 如果您需要我说得更清楚或举个例子,请告诉我。
    【解决方案2】:

    由于它们已排序,您只需传递一个索引即可开始搜索,这将避免 O(n^2)。您已经这样做了,但是通过将索引存储在全局变量中。相反,您应该将其作为参数传递给getIndexByAB

    function getIndexByAB(arr, a,b , initialIndex){
        // Was tracking last index by storing it in a global 'this.initialIndex'. 
        // 'this' points to 'window' in global functions. That's bad, it 
        // means this function can't be called on different arrays without
        // resetting the global 
    
        // var initialIndex =  this.initialIndex || 0,
    
        initialIndex = initialIndex || 0;
        var len = arr.length;
        for(initialIndex; initialIndex < len ; initialIndex++ ){
            var el = arr[initialIndex];
            if( el.b === b && el.a === a ){
                // Bad globals
                // this.initialIndex = initialIndex;
                return initialIndex;
            }
        }
        return -1;
    }
    
    var len = some.length;
    var lastValidIndex = 0;
    for(var i = 0; i < len ; i++){
        var el = some[i];
        // Pass the index here, so it doesn't start from scratch
        var index = getIndexByAB(full, el.a, el.b, lastValidIndex);
        if(index > -1) {
            full[index].c = true;
            lastValidIndex = index;
        }
    }
    

    顺便说一句,如果你确实想要一个函数来缓存一些值,下面是如何避免全局变量。 (不是说你应该在这种情况下使用它)

    var getIndexByAB = (function(){
         // This will only be executed once, and is private
         // to getIndexByAB (all invocations)
         var lastGoodIndex = 0;
    
         return function(arr, a,b, resetIndex){
             if (resetIndex) {
                lastGoodIndex = 0;
             }
    
             var len = arr.length;
             for(var index = lastGoodIndex; index < len ; index++ ){
                 var el = arr[index];
                 if( el.b === b && el.a === a ){                 
                     lastGoodIndex = index;
                     return index;
                 }
             }
             return -1;
        };
    })();
    

    或者,您可以通过将其缓存在getIndexByAB.initialIndex 中来实现以下目的,但这不是很优雅。避免这种情况的主要原因是getIndexByAB.initialIndex 可以被其他任何人修改

    【讨论】:

    • 您只删除了this 的一种用法。
    • @jrajav 谢谢,修复它.... 实际上,代码已经使用全局变量 this.currentIndex 保存当前索引。这很糟糕,因此,这是相同的代码,不依赖于全局变量。这意味着现在可以在多个数组上多次调用它
    • 谢谢胡安,我理解你对全局变量的看法。我虽然 this.XXX 作为函数的一个属性,但我已经用它来保存函数缓存很多次了。但是,我不明白您为什么将“i”传递给 getIndexByAB 插入“索引”。它假设索引会比 i 增长得更快。
    • @MartinBorthiry 不正确,我应该传递index 的最后一个值,修复它
    • 如果我在原来的方法中将this.initialIndex 替换为getIndexByAB.initialIndex 会怎样?它应该有效吗?
    【解决方案3】:

    没有@Juan 的回答那么有效(它利用了排序的性质等),但我认为我仍然会提出我的解决方案,因为它偶然迫使我想出一个克隆和比较 Javacript 的解决方案对象。

    实用程序

    // Create a copy of x without reference back to x
    function clone(x){
      return JSON.parse(JSON.stringify(x));
    }
    
    // Pass any number of arguments of any type. Returns true if they are all identical.
    function areEqual(){
      for(var i = 1, l = arguments.length, x = JSON.stringify(arguments[0]); i < arguments.length; ++i){
        if(x !== JSON.stringify(arguments[i])){
          return false;
        }
      }
    
      return true;
    }
    

    标记函数

    // Your flagLabel being 'c'
    function matchAndFlagWith(flagLabel,aFull,aSome){
      var aFlagged = clone(aFull);
    
      for(var i1 = 0, l1 = aSome.length, oSome; oSome = aSome[i1], i1 < l1; ++i1){
        for(var i2 = 0, l2 = aFlagged.length, oFlagged; oFlagged = aFlagged[i2], i2 < l2; ++i2){
          if(areEqual(oFlagged,oSome)){
            oFlagged[flagLabel] = true;
          }
        }
      }
    
      return aFlagged;
    }
    

    演示

    http://jsfiddle.net/barney/p2qsG/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-10
      • 1970-01-01
      • 1970-01-01
      • 2016-03-04
      • 2023-03-12
      • 2021-03-08
      • 1970-01-01
      • 2020-03-06
      相关资源
      最近更新 更多