'codewars Array.diff javascript

this is my first question as I'm learning programming for few days but now and I'm stuck

Task:

Your goal in this kata is to implement a difference function, which subtracts one list from another and returns the result.

It should remove all values from list a, which are present in list b keeping their order.

arrayDiff([1,2],[1]) == [2]
If a value is present in b, all of its occurrences must be removed from the other:

arrayDiff([1,2,2,2,3],[2]) == [1,3]

My solution:

function arrayDiff(a, b) {  
for (var j = 0; j < a.length; j++) {
  for (var i = 0; i < b.length; i++) {
    if(a[j] == b[i]) {
      a.splice(j);
      j--;
      }
    }
  }
  return a;
}

Looks like I'm doing something wrong and peculiar thing happens: sometimes tests turn all red, sometimes all BUT 1 test turn red, while rest is green. The one that always fails me is:

Should pass Basic tests

a was [1,2], b was [1]: expected [] to deeply equal [ 2 ]



Solution 1:[1]

The problem with your function is, that you change the length of your a array while in a for loop, which uses the length of a. This causes certain index to be skipped.

function arrayDiff(a, b) {  
for (var j = 0; j < a.length; j++) {
  for (var i = 0; i < b.length; i++) {
    if(a[j] == b[i]) {
      // here you change the size of a.
      a.splice(j);
      j--;
      }
    }
  }
  return a;
}

To fix this, create a temporary array, in which you push all values from a, which are not contained in b.

function arrayDiff(a,b) {
    // Temporary array, containing all values from a, which are not contained in b
    let diffArray = [];
    // Looping over a
    for(let i = 0; i < a.length; i++) {
        // Per default we say that b does not contain a
        let bContainsAValue = false;
        // Loop over b
        for(let y = 0; y < b.length; y++) {
            // If any value in b is the same as the current value from a, we set bContainsAValue to true
            if(a[i] === b[y]) bContainsAValue = true;
        }
        // Now only if bContainsAValue is still false, meaning it does not contain the a value, we push this value into our temporary array
        if(!bContainsAValue) diffArray.push(a[i]);
    }
    // In the end we return the temporary array
    return diffArray;
}

Here is my approach, which is simpler and uses some array functions:

function arrayDiff(a, b) {
    // Filter entire a array, only keep the value if b does not contain that value
    return a.filter(val => !b.includes(val));
}

Solution 2:[2]

Here is an easy approach that you can take to solve the error in your code with a mix of ES6.

Since your array length changes over the loop, you can use delete array[j] to remove the value without changing the length of the array or the index of elements. According to this answer

If you just want to make the element at index i no longer exist, but you don't want the indexes of the other elements to change:

This replaces the duplicate values with undefined. Then you can remove all the undefined elements with a single line

a.filter(Boolean)

Refer more here

function arrayDiff(a, b) {
  for (var j = 0; j < a.length; j++) {
    for (var i = 0; i < b.length; i++) {
      if (a[j] == b[i]) {
        delete a[j];
      }
    }
  }
  console.log(a.filter(Boolean))
  return a.filter(Boolean);
}
arrayDiff([1, 2], [1])
arrayDiff([1, 2, 2, 2, 3], [2])

Here's a single liner using ES6 without any nesting or mutation

const arrayDiff = (a, b) => a.filter(item => !b.includes(item))
console.log(arrayDiff([1, 2], [1]))
console.log(arrayDiff([1, 2, 2, 2, 3], [2]))

Solution 3:[3]

You could use the builtin methods for array, and combine that with the set. Something like the following:

function diff(a, b) {
  const left = a.filter(item => !b.includes(item))
  const right = b.filter(item => !a.includes(item))
  return Array.from(new Set([].concat(left, right)))
}

By your example

diff([1,2,2,2,3],[2]) // returns [1, 3]
diff([1,2],[1]) // returns [2]

The function should also be transitive, I.e the parameter order should not matter when calculating a difference. So flipping the parameters should return the same result.

diff([2], [1,2,2,2,3]) // returns [1, 3]
diff([1],[1,2]) // returns [2]

Solution 4:[4]

This also works:

#  create a function, pass in the listNames as parameters
def array_diff(a, b):
    # create an entity removeNum: the loop through b, to check if it's in list b
    for removeNumber in b:
    #  If removeNum is in b, loop through a as well to check if it's there
        while removeNumber in a:
    #  So, if removeNum is in list a
            try:
    #  Try function to remove it from a... using .remove()
                a.remove(removeNumber)
    #  except to return empty list
            except ValueError:
                return[]
    return a

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4 Atieno Obwanda