简体   繁体   中英

Taking the difference of two arrays and making a new array

I need this function to compare two arrays and return a new array that only contains elements that aren't common to both arrays. I wrote the following code:

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]);

but it returns [2,2,4] instead of just [4]

Also, I tried writing it using the filter method:

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]);

and that doesn't work for every instance either. I need to be able to compare arrays with numbers and strings as well. It has to be in javascript, no jquery for this.

you can try:

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]

When you splice the array all the elements move down in their position, this makes your loop jump a step. For example when it removes the 1 's in the 0th position the 2 's move to the new 0th part you already checked that it skips over next. A simple way to fix that is when you splice make sure you back step, you can decrement both i and j :

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

.splice mutates the array, so it messes with your loop. A simple fix for your code would be to iterate in reverse order:

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

  }
}

However that still has the problem that you are splicing arr1 too often. arr1 should only be spliced once per iteration:

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])); 

Sort the two arrays, remove duplicates from them, join the two arrays, sort the obtained array, and remove duplicates from the obtained array.

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;
        }

    }

}

You'll have a worst case time complexity of O((N+M)log(N+M)) . The solution you are trying works but is time complexity O(N*M).

EDIT

It turned out that since webkit (I did not investigate on other browsers) uses selection sort for sorting, this solution leads to a time complexity O(NM). In Java this approach should be faster though, as the engine can reach even O(N) for sorting reasonably short arrays, otherwise falling back on O(N log(N)) complexity for sorting.

Set a temporary object. Iterate over both arrays. For each element in each array, if that element is NOT a key within our temporary object, set a key in that object equal to that element with a value of true . If an element has already been visited, delete it.

At the end, return Object.keys(obj) to get an array of the keys.

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);
}

With less code than other answers:

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;
}

Edit : Comparison of some of the answers here: https://jsfiddle.net/c42wvvr5/2/

Seems this answer is fastest by far on Chrome, and slightly slower than Yang's on Firefox.

Converting array values to object keys can make it faster:

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;
}

I would choose the one with the simpler code though unless you have really huge arrays.

Here's a fun "reference" implementation. Its purpose is not to be particularly fast or efficient, but to be correct and to elucidate the algorithm from the code, which helps ensure correctness. You can write "better" versions and compare them against this one for correctness. "Automated testing is best testing." :-)

It does exactly what was stated in the question: elements -- not necessarily integers -- common to both arrays are expunged from both arrays. This means duplicate items in one array that aren't found in the other left untouched.

 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 ); 
 <pre id="output"></pre> 

That bit of HTML is just there to act as an "output device". I'm not an expert on the code snippet stuff.

Here's another way to see it: http://jsfiddle.net/Lp1zveba/

Addendum: I can't comment on other answers yet, but I like the one from ' rkho '; here's a comparison of it and my submission: http://jsfiddle.net/uuh7afLc/

It shows some discrepancies, which I would chalk up to a different interpretation of the question; I took it quite literally to mean that if an element is common between the two arrays, it ( and all its ilk ) will not appear in the result array. Some other answers use the alternate interpretation that for each pair of like objects, one is removed from each of the source arrays, and any unmatched leftovers in either array survive the journey to the result array.

Asker is encouraged to clarify the spec. :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM