1

I have two array, could be integer array, string array or even object array, for demo purpose, I'll use integer array.

array1:        array2:
 0              99
 1              1             
 101            5
 2              100
 100            97  
 5              101   
 4              4  

I want to have a function return the array include all the information about the difference between two arrays. result would be:

 0     --                 {match:false,leftIndex:0             }
 --    99                 {match:false:            rightIndex:0}
 1     1                  {match:true: leftIndex:1,rightIndex:1}
 --    5                  {match:false:            rightIndex:2}
 --    100                {match:false:            rightIndex:3}
 --    97                 {match:false:            rightIndex:4}
 101   101                {match:false:leftIndex:2,rightIndex:5}
 2     --                 {match:false:leftIndex:3             }
 100   --                 {match:false:leftIndex:4             }
 5     --                 {match:false:leftIndex:5             }
 4     4                  {match:false:leftIndex:6,rightIndex:6}

What is the best way to approach this? I'm planning to use this function to display a side by side view for those two array using angularJS directive method. will that work?

Peter Huang
  • 972
  • 4
  • 12
  • 34
  • Check - http://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript – Dharam Jul 01 '16 at 20:19
  • Levenshtein distance; https://en.wikipedia.org/wiki/Levenshtein_distance – Dmitry Bychenko Jul 01 '16 at 20:19
  • The Levenshtein distance is indeed the right direction. Can't find a ready npm package, but have a look at the knockout source code which does just that: https://github.com/knockout/knockout/blob/241c26ca82e6e4b3eaee39e3dc0a92f85bc1df0c/src/binding/editDetection/compareArrays.js#L20 – AlexG Jul 01 '16 at 20:21

2 Answers2

2

I suggest to use a Map for the values as key and indices as values for the matched check, if the delta of rightIndexand leftIndex is smaller than zero.

On this case, a matched item is found and if some other values from the right side are missing, they are picket up.

For the last part, possible leftover of array2 is pushed, too.

0   1   2   4   5  97  99 100 101   values
0   1   3   6   5   -   -   4   2   leftIndex
-   1   -   6   2   4   0   3   5   rightIndex
-   0   -   0  -3   -   -  -1   3   delta: p2 - p1
    *       *                   *   relevant only delta >= 0

var array1 = [0,     1,             101, 2, 100, 5, 4],
    array2 = [   99, 1, 5, 100, 97, 101,            4],
    map = new Map,
    j = 0,
    result = [];

array2.forEach(map.set.bind(map));

array1.forEach(function (a, i) {
    if (map.has(a) && map.get(a) >= i) {
        while (j < map.get(a)) {
            result.push({ value: array2[j], match: false, rightIndex: j });
            j++;
        }
        result.push({ value: a, match: true, leftIndex: i, rightIndex: j });
        j++;
        return;
    }
    result.push({ value: a, match: false, leftIndex: i });
});

while (j < array2.length) {
    result.push({ value: array2[j], match: false, rightIndex: j });
    j++;
}

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

If you can use last ES6 features I'll go with something like this:

function areArrayEqual(array1, array2) {
    // Based on http://stackoverflow.com/a/14853974/2586392
    if (array1.length !== array2.length) return false;

    for (var i = 0, l=array1.length; i < l; i++) {
      if (!areArrayEqual(array1[i], array2[i]) return false;       
      else if (array1[i] != array2[i]) return false;   
    }       

    return true;
})

var result = [];
array1.forEach((value, i) => {
  let j = array2.findIndex(x => {
    if (typeof value !== typeof x) return false;
    if (typeof value === 'object') return areArrayEquals(Object.values(x), Object.values(value));
    if (typeof value === 'array') return areArrayEquals(x, value);
    return x === value;
  });

  result.push([value, j === -1 ? '---' : value , {
    match: !!(j + 1),
    leftIndex: i,
    rightIndex: j === -1 ? '---' : j
  }]);

  if (j !== -1) {
    delete array2[j];
  }
});

array2.forEach((value, i) => {
  result.push(['---', value, {match: false, leftIndex: '---', rightIndex: i}]);
});

Otherwise you can still use something similar, but you need to polyfill Object.values and maybe forEach as well

rpadovani
  • 7,101
  • 2
  • 31
  • 50