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?
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;
}
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
- Why Zero Breaks the Loop
- Multiple Solutions
- Performance Comparison
- Best Practices
- Alternative Approaches
Understanding the Problem
The problematic code uses a for loop with a condition that relies on the truthiness of the assigned value:
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:
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
"" nullundefinedfalse
In your code:
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:
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:
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:
// 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:
-
Avoid modifying Array.prototype unless it’s absolutely necessary, as it can affect all arrays in your application and potentially cause conflicts.
-
Use the Set approach for modern JavaScript environments (ES6+):
function getUniqueArray(arr) {
return Array.from(new Set(arr));
}
-
Consider the data types - if you have objects or complex values, you’ll need a different approach.
-
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:
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:
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
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]toi < this.lengthin 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.