Why does Array.filter(Number) filter zero out in JavaScript?
I'm trying to filter all non-numeric elements out from an array. We can see the desired output when using typeof. But with Number, it filters zero out.
Here's the example (tested in Chrome Console):
[-1, 0, 1, 2, 3, 4, Number(0), '', 'test'].filter(Number)
// Which output with zero filtered out:
[-1, 1, 2, 3, 4] // 0 is filtered
If we use typeof, it doesn't filter zero, which was expected.
// code
[-1, 0, 1, 2, 3, 4, Number(0), '', 'test'].filter(n => typeof n === 'number')
// output
[-1, 0, 1, 2, 3, 4, 0]
My question:
What is the difference between the 'Number' and 'typeof' approaches?
Number filters zero, but 'Number' itself literally contains zero, and this confuses me.
Solution 1:
Because 0
is one of the many falsy
values in javascript
All these conditions will be sent to else
blocks:
if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if ('')
if ("")
if (``)
From the Array.prototype.filter()
documentation:
filter()
calls a providedcallback
function once for each element in an array, and constructs a new array of all the values for which callback returns a value that coerces to true
In your case the callback function is the Number
. So your code is equivalent to:
[-1, 0, 1, 2, 3, 4, Number(0), '', 'test'].filter(a => Number(a))
// Number(0) -> 0
// Number(Number(0)) -> 0
// Number('') -> 0
// Number('test') -> NaN
When filter
function picks truthy values (or values that coerces to true
), the items which return 0
and NaN
are ignored. So, it returns [-1, 1, 2, 3, 4]
Solution 2:
To prevent a falsy zero from filtering, you could use another callback for getting only numerical values: Number.isFinite
console.log([-1, 0, 1, 2, 3, 4, Number(0), '', 'test'].filter(Number.isFinite))
Solution 3:
Expected behavior
This behavior isn't unique to using Number as the filter function. A filter function that simple returns the 0
value would also remove it from the list.
var a = [-1, 0, 1, 2, 3, 4, Number(0), '', 'test'].filter(v => v)
console.log(a); // [-1, 1, 2, 3, 4, "test"]
This is because Number
isn't specifically a filter function, it's primarily a type-casting function (and a class constructor, but not a very useful one). So when a number (like 0
) is passed to Number
, it just returns that number.
Array.prototype.filter
removes values that are falsy. In JavaScript, the following are falsy and thus removed by filter
.
false
null
undefined
0
NaN
''
""
``
(For complicated backwards compatibility reasons MDN goes into, document.all
is also falsy in many browsers despite being an object, but that's a side-note)