javascriptarraysalgorithmsymmetric-difference

Comparing 2 arrays for differences (find symmetric difference)


This should be a simple algorithm but I can't wrap my head around the logic. I need to take 2 arrays and keep only the values that are found in one array or the other and not both.

For example if I have

arr1 [1,2,3,6]

arr2 [2,3,4,5,7]

I want to have 2 arrays again being

arr1 [1,6]
arr2 [4,5,7]

EDIT:

I removed my old code and put in some other working code. Still not very pretty but at least working. All I would need to do is store arr3 into arr1 and arr4 into arr2.

    var arr3 = [];
    var arr4 = [];

    var foundMatch = false;

    //find all arr1 elements that don't match arr2 elements
    //store into arr4
    for(var i=0; i<arr1.length; i++)
    {
        foundMatch = false;
        for(var j=0; j<arr2.length; j++)
        {
            if(arr1[i] == arr2[j])
            {
                console.log("match found for [%s] [%s]", i, j);
                foundMatch = true;
                break;
            }
        }
        if(!foundMatch)
        {
            arr4.push(arr1[i]);
        }
    }

    //find all arr2 elements not in arr1
    //store in arr3
    for(var i=0; i<arr2.length; i++)
    {
        foundMatch = false;
        for(var j=0; j<arr1.length; j++)
        {
            if(arr2[i] == arr1[j])
            {
                console.log("match found for [%s] [%s]", i, j);
                foundMatch = true;
                break;
            }
        }
        if(!foundMatch)
        {
            arr3.push(arr2[i]);
        }
    }

Solution

  • The fastest solution in terms of time complexity is to create a Set or a hash from one array -> O(N), then iterate through the other to compare if an element is not the set -> O(M) -> and then add it to the diffs array. The main benefit here is that Set lookup time is constant. This solution also has space complexity of O(N) for building the set.

    You can repeat this for the other array as well, giving you total of O(N) + O(M) + O(N) + O(M) which, when constant factors are removed, is just O(N+M) time complexity.

    Building the set for the second array gives total space complexity as O(N+M).

    // Total complexity
    // Time:  O(N+M)
    // Space: O(N+M)
    
    // time complexities for each step shown bellow
    
    let N = [1,2,3,6]
    let M = [2,3,4,5,7]
    
    const setForN = new Set(N); // O(N)
    const setForM = new Set(M); // O(M)
    
    // O(N + M) at this point
    
    const answerN = N.filter(n => !setForM.has(n)) // O(N)
    const answerM = M.filter(m => !setForN.has(m)) // O(M)
    
    // O(N + M) + O(N + M) at this point, which is just O(N + M)
    
    console.log(answerN, answerM);

    ES5 solution:

    var N = [1,2,3,6]
    var M = [2,3,4,5,7]
    
    var setOfNumbers = function(arr) {
      return arr.reduce(function(set, item) {
        return Object.defineProperty(set, item, { value: item });
      }, Object.create(null));
    }
    var setForN = setOfNumbers(N);
    var setForM = setOfNumbers(M);
    
    var answerN = N.filter(function(n) { return typeof setForM[n] !== 'number' });
    var answerM = M.filter(function(m) { return typeof setForN[m] !== 'number' });
    
    console.log(answerN, answerM);


    Another way is to do what other answers suggest which is to, for each element in N -> O(N), check if it exists in M -> O(M), and add it to the diffs array. Repeat for the other array.

    This has better, constant, space complexity O(1) but slower quadratic O(N*M) time.

    // Total Complexity:
    // Time:  O(N*M)
    // Space: O(1)
    
    let N = [1,2,3,6]
    let M = [2,3,4,5,7]
    
    // For every element in N -> O(N)
    // Iterate through M and check if it's there -> O(M)
    // Total: O(N*M)
    const answerN = N.filter(n => !M.includes(n));
    
    // For every element in M -> O(M)
    // Iterate through N and check if it's there -> O(N)
    // Total: O(M*N)
    const answerM = M.filter(m => !N.includes(m));
    
    // O(N*M) + O(M*N) at this point, which is just O(N*M)
    
    console.log(answerN, answerM);