NeuroAgent

JavaScript Remove Duplicates: Handle Zero Values

Learn why JavaScript array duplicate removal fails with zeros and discover multiple solutions. Complete guide to fixing prototype methods and modern ES6 approaches for unique array values.

Question

Get all unique values in a JavaScript array (remove duplicates)

I have an array of numbers that I need to make sure are unique. I found the code snippet below on the Internet, and it works great until the array has a zero in it. I found another script that looks almost exactly like it, but it doesn’t fail.

How can I determine where the prototype script is going wrong?

javascript
Array.prototype.getUnique = function() {
  var o = {}, a = [], i, e;
  for (i = 0; e = this[i]; i++) {o[e] = 1};
  for (e in o) {a.push (e)};
  return a;
}
NeuroAgent

The issue in your Array.prototype.getUnique method occurs because of how JavaScript evaluates conditions in the for loop. The problem is in this line: for (i = 0; e = this[i]; i++) {o[e] = 1}. When the array contains a zero, the loop terminates prematurely because zero is falsy in JavaScript.

Here’s why this happens: the condition e = this[i] assigns the array element to e and returns that value. If the value is zero (which is falsy), the loop stops executing, even though there may be more elements after the zero that need to be processed.


Contents


Understanding the Problem

The problematic code uses a for loop with a condition that relies on the truthiness of the assigned value:

javascript
Array.prototype.getUnique = function() {
  var o = {}, a = [], i, e;
  for (i = 0; e = this[i]; i++) {o[e] = 1};  // Problem here!
  for (e in o) {a.push (e)};
  return a;
}

When you test this with an array containing zero, you’ll see the issue:

javascript
const testArray = [1, 2, 0, 3, 2, 4];
console.log(testArray.getUnique()); // Expected: [1, 2, 0, 3, 4]
                                      // Actual: [1, 2]

The loop stops at index 2 (where the value is 0) and never processes the remaining elements.

Why Zero Breaks the Loop

JavaScript has specific truthy and falsy values:

Truthy values:

  • Non-zero numbers
  • Non-empty strings
  • Objects
  • Arrays
  • Functions
  • true

Falsy values:

  • 0, -0, 0.0 (all zero numbers)
  • Empty string ""
  • null
  • undefined
  • false

In your code:

javascript
for (i = 0; e = this[i]; i++) {
  // When this[i] is 0, e becomes 0, which is falsy
  // So the loop condition fails and stops
}

Multiple Solutions

Solution 1: Use Array Length Instead of Truthiness

The most straightforward fix is to use the array length in the condition:

javascript
Array.prototype.getUnique = function() {
  var o = {}, a = [], i, e;
  // Fixed: use i < this.length instead of e = this[i]
  for (i = 0; i < this.length; i++) {o[this[i]] = 1};
  for (e in o) {a.push (e)};
  return a;
}

Solution 2: Explicit Check for Undefined

Another approach is to check specifically for undefined:

javascript
Array.prototype.getUnique = function() {
  var o = {}, a = [], i, e;
  for (i = 0; e = this[i]; i++) {
    // Only break if we actually reached the end
    if (typeof e === 'undefined') break;
    o[e] = 1;
  }
  for (e in o) {a.push (e)};
  return a;
}

Solution 3: Use Modern JavaScript Methods

For cleaner code, consider using modern JavaScript approaches:

javascript
// Using Set (ES6)
Array.prototype.getUnique = function() {
  return [...new Set(this)];
}

// Using filter and indexOf
Array.prototype.getUnique = function() {
  return this.filter((item, index) => {
    return this.indexOf(item) === index;
  });
}

Performance Comparison

Here’s a comparison of different approaches:

Method Time Complexity Space Complexity Handles Zero?
Original (problematic) O(n) O(n) ❌ No
Fixed version O(n) O(n) ✅ Yes
Set approach O(n) O(n) ✅ Yes
Filter/IndexOf O(n²) O(n) ✅ Yes

The fixed version and Set approach are optimal for performance. The filter/IndexOf method is less efficient for large arrays due to its O(n²) complexity.

Best Practices

When working with array uniqueness in JavaScript:

  1. Avoid modifying Array.prototype unless it’s absolutely necessary, as it can affect all arrays in your application and potentially cause conflicts.

  2. Use the Set approach for modern JavaScript environments (ES6+):

javascript
function getUniqueArray(arr) {
  return Array.from(new Set(arr));
}
  1. Consider the data types - if you have objects or complex values, you’ll need a different approach.

  2. Be mindful of browser support - Set is supported in all modern browsers but not in older ones like IE.

Alternative Approaches

For Object Arrays

If you need to remove duplicates from object arrays based on a property:

javascript
function getUniqueObjects(arr, key) {
  const seen = new Set();
  return arr.filter(obj => {
    const value = obj[key];
    if (seen.has(value)) {
      return false;
    }
    seen.add(value);
    return true;
  });
}

For Mixed Data Types

If your array contains different data types and you want to preserve types:

javascript
function getUniqueMixed(arr) {
  const seen = new Map();
  return arr.filter(item => {
    const key = typeof item + JSON.stringify(item);
    if (seen.has(key)) {
      return false;
    }
    seen.set(key, true);
    return true;
  });
}

The key takeaway is that when using assignment expressions in loop conditions, always be aware of what values might be falsy in JavaScript, and prefer explicit length checks or other more robust approaches.


Sources

  1. MDN - Truthy and Falsy values
  2. MDN - For…of statement
  3. MDN - Set object
  4. JavaScript.info - Comparisons

Conclusion

  • The issue occurs because zero is falsy in JavaScript, causing the loop condition to fail prematurely
  • The simplest fix is changing e = this[i] to i < this.length in the for loop condition
  • Modern JavaScript provides cleaner solutions using Set objects
  • Be cautious when modifying Array.prototype as it can have side effects
  • Consider the performance implications of different approaches, especially for large arrays
  • For most use cases, the Set approach ([...new Set(arr)]) is recommended for its simplicity and performance

By understanding JavaScript’s truthy/falsy evaluation, you can avoid similar issues in your code and choose the most appropriate method for removing duplicates from arrays.