I'd like to post a "meta"-answer, pointing at some archetypal mistakes made by you and other posters and frequently seen in other code reviews.
Mistake 1: unary +
Unary plus seriously hurts readability, especially in combination with other operators. Please do your readers (including your a few months older self) a favor and use the Number
function - this is what it's for:
+a + +b // wtf?
Number(a) + Number(b) // copy that
Apart from readability, Number(x)
is identical to +x
in every way.
Mistake 2: not checking for NaN
s
Number conversions can fail, and when they fail, they return a NaN
and NaN
s are sticky, so this will return NaN
despite valid numbers being present in the array:
[1, 2, 'blah'].reduce((a, b) => Number(a) + Number(b)) // =NaN
This will be better (in the context of summation, NaN
can be considered 0
):
[1, 2, 'blah'].reduce((a, b) => (Number(a) || 0) + (Number(b) || 0)) // =3
Mistake 3: not checking for empty values
Unfortunately, Number
is broken in javascript. For "historical reasons" it returns 0
when given null
or an empty string. For the summation function it doesn't matter, but it will bite you once you decide to use similar code for multiplication.
Mistake 4: reduce with no initial value
array.reduce(func)
looks tempting, but unfortunately it doesn't work with empty arrays
[].reduce((a, b) => a + b) // TypeError: Reduce of empty array with no initial value
so consider the init mandatory:
[].reduce((a, b) => a + b, 0) // returns 0
Mistake 5: wrong iteration method
The choice between iteration methods (forEach/map/filter/reduce
) is tough sometimes, but this simple set of rules should help in most cases:
- use
map
to convert a number of things to the same number of other things
- use
filter
to convert a number of things to a lesser number of the same things
- use
reduce
to convert a number of things to one other thing
- do not use
forEach
For example, this:
result = [];
array.forEach(item => result.push(do_something(item))) //
is an "antipattern" and should actually be map
:
result = array.map(do_something) //
Similarly, this
result = 0;
array.map(item => result = result + item)) //
should be
result = array.reduce((res, item) => result + item, 0) //
Putting it all together
Our assignment basically consists of three parts:
- convert all elements in the array to numbers
- remove those that couldn't be converted
- sum the rest
For the first step we use map
, then filter
, then reduce
:
let sumNumbers = a => a
.map (x => Number(x)) // convert to numbers
.filter (x => !Number.isNaN(x)) // remove NaN's
.reduce ((s, x) => s + x, 0) // sum
On a more advanced note, with a couple of helpers we can also write this "point-free", without arrow functions:
let not = fn => x => !fn(x);
let add = (x, y) => x + y;
let sumNumbers = a => a
.map(Number)
.filter(not(Number.isNaN))
.reduce(add, 0)