Given an array and a value, remove all instances of that value in place and return the new length

Viewed 284

It is not a duplicate as I don't seek the answer to a specific problem, I want to understand why my answer to a problem fails.

I'm trying to solve a problem on LeetCode and the functions I create yield correct results on the Chrome DevTools but when I submit them on the website they fail.

I want to understand why they fail and/or aren't accepted.

Example 1:

Given nums = [3,2,2,3], val = 3,

Your function should return length = 2, with the first two elements of nums being 2.

It doesn't matter what you leave beyond the returned length.

Example 2:

Given nums = [0,1,2,2,3,0,4,2], val = 2,

Your function should return length = 5, with the first five elements of nums containing 0, 1, 3, 0, and 4.

Note that the order of those five elements can be arbitrary.

It doesn't matter what values are set beyond the returned length.

My provided answers:

var removeElement = function(nums, val) {
    return nums.filter(e => e !== val).length;
};

and

var removeElement = function(nums, val) {
    for (i = 0; i < nums.length; i += 1) {
        if (nums[i] === val) {
            nums.splice(i, 1)
        }
    }
    return nums;
};

Working answer based off the solutions:

var removeElement = function(nums, val) {
    for (i = nums.length; i >= 0; i-= 1) {
        if (nums[i] === val) {
            nums.splice(i, 1);
        }
    }
    return nums.length;
};
4 Answers

Your first solution fails because you're not removing the items "in place". Currently, your original array remains unmodified and instead, you're returning an entirely new array (as .filter() creates and returns this new array).

In your second solution, you're not considering what happens when you remove an item from your array. In fact, your second solution doesn't work (for example removing 3 from [1, 2, 3, 3, 4]). The reason for this is because when you remove an item from your array the indexes in the array will shift. For example, if you remove an element when i is 2, then the item that was previously at index 3 will shift to index 2 after the deletion, same with all other indexes greater than 2. As a result, when you increment i, to 3, you will skip the value which shifted to index 2.

Visual example of the above:

removing = 3
i = 2
       i
 0  1  2  3  4
[1, 2, 3, 3, 4]

After removal your array is:

 0  1  2  3
[1, 2, 3, 4]

You then proceed to the next iteration, so i increments to 3:

removing = 3
i = 3
          i
 0  1  2  3
[1, 2, 3, 4]
       ^--- Skipped!

Another small issue with your last solution is that you're supposed to return the length of the array, not the array itself.

The first approach does not mutate the given array.

Your second approach does not remove all unwanted items,

[0, 1, 2, 3, 0, 4]

because with removing an item, you get the next value at the same index, but you increment the index.

You could loop from the end to avoid this.

const
    nums = [0, 1, 2, 2, 3, 0, 4, 2],
    removeElement = function(nums, val) {
        for (i = nums.length - 1; i >= 0; i--) {
            if (nums[i] === val) nums.splice(i, 1);
        }
        return nums.length;
    };
    
console.log(removeElement(nums, 2));
console.log(nums);

  1. The first approach is creating a new array instead of mutating the original array.
  2. The second approach is mutating the array while the looping is being executed, so the loop will be inconsistent.

You can use the function Array.prototype.reduce for counting the values different than the variable val.

const nums = [0, 1, 2, 2, 3, 0, 4, 2],
      val = 2,
      removeElement = nums.reduce((a, n) => a + Boolean(n !== val), 0);
    
console.log(removeElement);

If you want to mutate, you can modify the array in place within the function as follow

const arr = [0, 1, 2, 2, 3, 0, 4, 2],
      removeElement = function(nums, val) {
        let desiredValues = nums.filter(n => n !== val);

        nums.length = 0;
        nums.splice(0, 0, ...desiredValues);
      };
      
removeElement(arr, 2);
console.log(arr);

You may use .indexOf() :

var nums = [0, 1, 2, 2, 3, 0, 4, 2];

var removeElement = function(nums, val) {
    while ((i = nums.indexOf(val)) != -1) {
        nums.splice(i, 1);
    }
    return nums.length;
};
console.log(removeElement(nums, 2));
console.log(nums);

Another solution can use double indexes:

const nums = [0, 1, 2, 2, 3, 0, 4, 2];

const removeElement = function(nums, val) {
    for(i=0, j=nums.length-1; i<j;) {
        while(nums[i] != val) {
            i++;
        }
        while(nums[j] == val) {
            j--;
        }
        if (i<j) {
            nums[i] = nums[j];
            nums[j] = val;
        }
    }
    nums.length = i;
    return nums.length;
};


console.log(removeElement(nums, 2));
console.log(nums);

Related