【问题标题】:Taking the difference of two arrays and making a new array取两个数组的差并制作一个新数组
【发布时间】:2015-06-02 01:52:05
【问题描述】:

我需要这个函数来比较两个数组并返回一个新数组,该数组只包含两个数组不共有的元素。我写了以下代码:

function diff(arr1, arr2) {

  var newArray = [];

  for (var i=0;i<arr1.length;i++){
    for (var j=0;j<arr2.length;j++){
      if (arr1[i]===arr2[j]){

        arr1.splice(i,1);
        arr2.splice(j,1);
      }
    }
  }
  newArray = arr1.concat(arr2);
  return newArray;
}

diff([1, 2, 3, 5], [1, 2, 3, 4, 5]);

但它返回 [2,2,4] 而不仅仅是 [4]

另外,我尝试使用过滤方法编写它:

function diff(arr1, arr2) {

  var newArray = [];

  function compare(x){
    for (var i = 0;i<arr2.length;i++){
      return x != arr2[i];
    }
  }
  newArray = arr1.filter(compare);
  return newArray; 
}

diff([1, 2, 3, 5], [1, 2, 3, 4, 5]);

这也不适用于每个实例。 我还需要能够将数组与数字和字符串进行比较。 它必须在 javascript 中,没有 jquery。

【问题讨论】:

  • “它必须在 javascript 中,没有 jquery。” jQuery JavaScript。然而,jQuery 的领域是 DOM 操作,而不是处理集合。
  • 你的实现很奇怪。如果在纸上为您提供了 2 个数字列表并且您需要生成第三个差异列表,您将如何手动执行此操作?
  • 数字和字符串?意思是你想保留类型?

标签: javascript


【解决方案1】:

你可以试试:

function diff(arr1, arr2) {

    var tmp = [];
    var newArray = [];

    for (var i = 0; i < arr1.length; i++) {
        for (var j = 0; j < arr2.length; j++) {
            if (arr1[i] === arr2[j]) {
                arr1[i] = arr2[j] = null;
            }
        }
    }
    tmp = arr1.concat(arr2);
    for (var i = 0; i < tmp.length; i++) {
        if (tmp[i] !==null) {
            newArray.push(tmp[i]);
        }
    }
    return newArray;
}

console.log(diff([0,1,2], [1,2]));//output: [0]

【讨论】:

  • 不适用于diff([0,1,2], [1,2]),但总体思路还不错。
  • 至少适用于数字和刺痛 :) 您可能想要添加更多解释,而不仅仅是“试试这个”。
  • 感谢几个答案...我一直坚持这一点
  • O(N*M) 很简单,加油:)
【解决方案2】:

当您splice 数组时,所有元素都向下移动到它们的位置,这使您的循环跳了一步。例如,当它删除0th 位置中的1 时,2 将移动到您已经检查过的新0th 部分,它会跳过下一个。解决此问题的一种简单方法是,在拼接时确保退一步,您可以同时减少 ij

  if (arr1[i]===arr2[j]) {
    arr1.splice(i,1);
    arr2.splice(j,1);
    i--; 
    j--;  
  }

【讨论】:

  • @FelixKling 我明白你的意思,你介意提供一个失败的例子吗?
  • 实际上,根据期望的结果,也许它不会“失败”。我的评论是针对具有重复元素的数组。示例:jsfiddle.net/2b9hLrhj。在这里,2 包含在最终数组中,即使它出现在它们两者中。如果您单独考虑每个元素而不考虑其值,这是正确的。如果一个人对数组有更多的“设置视角”,那么2 可能不应该包含在最终数组中。但是,如果一个数组包含两次4 怎么办? 4 应该在结果数组中出现两次还是一次?我可能想多了,走哪条路取决于
  • @FelixKling 是的,我在考虑考虑每个元素而不考虑价值。例如,我在想[1,1]-[1] -&gt; [1]。但这仅仅是基于 OP 想要做的事情。
  • 是的,现在对我来说很有意义。抱歉让事情复杂化了。既然你在修改i,那就没问题了。如果我没有在我的解决方案中移动那个调用,那真的会产生错误的结果。
【解决方案3】:

.splice 会改变数组,因此它会干扰您的循环。您的代码的一个简单修复方法是以相反的顺序进行迭代:

for (var i=arr1.length; i--;){
  for (var j=arr2.length; j--){

  }
}

不过还是有你拼接arr1太频繁的问题。 arr1 每次迭代只能拼接一次:

for (var i=arr1.length; i--;){
  var same = false;
  for (var j=arr2.length; j--;){
    if (arr1[i] === arr2[j]) {
      same = true;
      arr2.splice(j, 1);
      break;
    }
  }
  if (same) {
    arr1.splice(i, 1);
  }
}

function diff(arr1, arr2) {
  for (var i = arr1.length; i--;) {
    var same = false;
    for (var j = arr2.length; j--;) {
      if (arr1[i] === arr2[j]) {
        same = true;
        arr2.splice(j, 1);
        break;
      }
    }
    if (same) {
      arr1.splice(i, 1);
    }
  }
  return arr1.concat(arr2);
}

console.log(diff([1, 2, 3, 5], [1, 2, 3, 4, 5]));

【讨论】:

  • diff([1,1], [2]) 似乎正确地产生了[1,1,2]
  • @DondiMichaelStroma 看起来正确,基于问题的标准
【解决方案4】:

代码比其他答案少:

function diff(arr1, arr2) {
  var newArray = [];
  for (var i=0;i<arr1.length;i++) {
    if (arr2.indexOf(arr1[i]) == -1) newArray.push(arr1[i]);
  }
  for (var i=0;i<arr2.length;i++){
    if (arr1.indexOf(arr2[i]) == -1) newArray.push(arr2[i]);
  }
  return newArray;
}

编辑: 比较这里的一些答案: https://jsfiddle.net/c42wvvr5/2/

似乎这个答案在 Chrome 上是迄今为止最快的,比杨在 Firefox 上的稍慢。

将数组值转换为对象键可以使其更快:

function diff_dms2(arr1, arr2) {
  var newArray = [];
  var ob1 = { };
  var ob2 = { };
  for (var i=0; i<arr1.length; i++) ob1[arr1[i]] = true;
  for (var i=0; i<arr2.length; i++) ob2[arr2[i]] = true;
  for (var i=0;i<arr1.length;i++) {
    if (!ob2[arr1[i]]) newArray.push(arr1[i]);
  }
  for (var i=0;i<arr2.length;i++){
    if (!ob1[arr2[i]]) newArray.push(arr2[i]);
  } 
  return newArray;
}

除非你有非常大的数组,否则我会选择代码更简单的那个。

【讨论】:

  • 到底有多快?
  • 看来你的是最慢的,我的是最快的但错误(包含null 值:-/)。
  • @FelixKling 速度似乎在很大程度上取决于实现,Chrome 和 Firefox 显示相反的结果。我应该预料到的 ;)
  • 啊,是的,Chrome 和 Firefox 的区别很大。 编辑: 我也是:D
  • 请不要误会我的意思,我的评论并不怀疑您的代码可能会更高效(尽管您仍在执行 2 nm 循环,indexOf 不会出现便宜),但*说这很容易。我更愿意解释为什么它会/可能会更快。
【解决方案5】:

对两个数组进行排序,去除重复项,将两个数组连接起来,对得到的数组进行排序,从得到的数组中去除重复项。

function getArraysDifference(a1, a2) {

    sortAndRemoveDuplicates(a1);
    sortAndRemoveDuplicates(a2);
    var outputArray = a1.concat(a2)
    sortAndRemoveDuplicates(outputArray);
    return outputArray; 

}

function sortAndRemoveDuplicates(inputArray) {

    inputArray.sort();
    var lastValue = inputArray[0];
    var i = 0;
    var currentValue;

    for (i = 1; i < inputArray.length; i++) {

        currentValue = inputArray[i];

        if (currentValue === lastValue) {
            inputArray.splice(i, 1);
            i--;
        } else {
            lastValue = currentValue;
        }

    }

}

最坏情况下的时间复杂度为 O((N+M)log(N+M))。您尝试的解决方案有效,但时间复杂度为 O(N*M)。

编辑

原来,由于webkit(我没有在其他浏览器上调查)使用选择排序进行排序,这个解决方案导致时间复杂度O(N M)。 但在 Java 中,这种方法应该更快,因为引擎甚至可以达到 O(N) 来对合理短的数组进行排序,否则会退回到 O(N log(N)) 的排序复杂度。

【讨论】:

  • 这仅适用于具有唯一元素的数组。
  • 只保留对最后找到的不同元素的引用。即使有重复的元素,它也能轻松工作。
  • 我不相信这一点。假设我有[1,2,2,3][1,4],我会得到[1,1,2,2,3,4],根据你的描述,我会得到[3,4] 而不是[2,2,3,4]。我是不是误会了什么?
  • @Felix 你是对的。首先对两个数组进行排序,删除重复项,加入它们,对新数组进行排序并继续就足够了。我已经编辑了答案。还是比公认的答案快不是吗? :)
【解决方案6】:

这是一个有趣的“参考”实现。它的目的不是特别快或特别高效,而是正确并从代码中阐明算法,这有助于确保正确性。您可以编写“更好”的版本并将它们与这个版本进行比较以确保正确性。 “自动化测试是最好的测试。” :-)

它完全按照问题中所述:两个数组共有的元素(不一定是整数)从两个数组中删除。这意味着一个数组中的重复项在另一个数组中找不到。

function arrayDiff( array1, array2 ) {
    var r1 = array1.slice();
    var r2 = array2.slice();
    var r1Unique = []; // collector for known "unique" values
    while( !r1.every( function( r1Obj ) {
        var uniqueR1Obj = r2.every( function( r2Obj ) {
            if( r1Obj !== r2Obj ) {
                return true; // unique so far, keep processing
            }
            // collision: remove all references to this object in R2
            var newR2 = [];
            r2.forEach( function( oldR2Obj ) {
                if( oldR2Obj !== r2Obj ) {
                    newR2.push( oldR2Obj );
                }
            } );
            r2 = newR2; // start operating on the new smaller array
            return false; // uniqueness lost, bail
        } );
        if( uniqueR1Obj ) {
            r1Unique.push( r1Obj );
        }
        // remove all references to this object in R1
        var newR1 = [];
        r1.forEach( function( oldR1Obj ) {
            if( oldR1Obj !== r1Obj ) {
                newR1.push( oldR1Obj );
            }
        } );
        r1 = newR1; // start operating on the new smaller array
        return uniqueR1Obj; // keep processing if still unique, or bail
    } ) );
    return r1Unique.concat( r1.concat( r2 ) );
}

function addTestResults( r1, r2 ) {
    var s = 'arrayDiff( [ ' + r1 + ' ], [ ' + r2 + ' ] ) == [ ' + arrayDiff( r1, r2 ) + ' ]<br/>';
    el = document.getElementById( "output" );
    if( el !== null )
        el.innerHTML += s;
}

var testArray1 = [ 1, 2, 4, 8, 16, 32, 0 ];
var testArray2 = [ 1, 1, 3, 8, 15 ];
var testArray3 = [ 32, 3, 8, 0, 8, 8, 15 ];
            
addTestResults( testArray1, testArray2 );
addTestResults( testArray2, testArray3 );
addTestResults( testArray1, testArray3 );
&lt;pre id="output"&gt;&lt;/pre&gt;

那段 HTML 只是用来充当“输出设备”。我不是代码 sn-p 方面的专家。

这是另一种查看方式:http://jsfiddle.net/Lp1zveba/

附录:我还不能评论其他答案,但我 like the one from 'rkho';这是它和我提交的比较:http://jsfiddle.net/uuh7afLc/

它显示了一些差异,我会将其归结为对问题的不同解释;我从字面上理解它的意思是,如果一个元素在两个数组之间是通用的,那么它(及其所有同类)将不会出现在结果数组中。其他一些答案使用另一种解释,即对于每一对相似的对象,从每个源数组中删除一个,并且任一数组中的任何不匹配的剩余部分在到达结果数组的过程中仍然存在。

鼓励提问者澄清规范。 :-)

【讨论】:

    【解决方案7】:

    设置一个临时对象。遍历两个数组。对于每个数组中的每个元素,如果该元素不是我们临时对象中的键,则将该对象中的键设置为等于该元素的值true。如果某个元素已被访问过,请将其删除。

    最后,返回 Object.keys(obj) 得到一个键的数组。

    function differenceOfTwoArrays(arr1, arr2) {
      var obj = {};
    
      arr1.forEach(function(elem) {
        if (!obj[elem]) {
          obj[elem] = true;
        } else if (obj[elem]) {
          delete (obj[elem]);
        }
      });
    
      arr2.forEach(function(elem) {
        if(!obj[elem]) {
          obj[elem] = true;
        } else if (obj[elem]) {
          delete (obj[elem]);
        }
      });
    
      return Object.keys(obj);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 2013-07-26
      • 1970-01-01
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多