Reputation: 119
I have a function that needs to sum all numbers in an arrays, if those numbers are strings '1', '2' the function needs to sum those too.
I have written a function and tried parsing the numbers but it doesn't work.
basically, it needs to sum up all numbers. Can you do it without using reduce
? I need a simple solution or a solution with .map Where is my mistake?
function sumArrNums(arr) {
let count = 0;
arr.forEach(el => typeof el == 'number' ? count+= el : '');
return count;
}
console.log(sumArrNums(['1', '2', 5,5,5, '3']))
Upvotes: 3
Views: 366
Reputation: 214949
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.
+
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.
NaN
sNumber 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
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.
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
The choice between iteration methods (forEach/map/filter/reduce
) is tough sometimes, but this simple set of rules should help in most cases:
map
to convert a number of things to the same number of other thingsfilter
to convert a number of things to a lesser number of the same thingsreduce
to convert a number of things to one other thingforEach
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) // 👍
Our assignment basically consists of three parts:
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)
Upvotes: 2
Reputation: 22776
Your ternary operator is doing nothing when the element is a string, you can use Number(el)
(or unary +
) to convert elements to numbers (strings will be converted, and numbers will remain numbers, so there is no need for type checking):
function sumArrNums(arr) {
let count = 0;
arr.forEach(el => count += Number(el));
return count;
}
console.log(sumArrNums(['1', '2', 5, 5, 5, '3']))
Upvotes: 3
Reputation: 1074208
Your code is okay, you just need to ensure that you coerce the strings to number. There are lots of ways to do that, in your case you might use the unary +
:
function sumArrNums(arr) {
let count = 0;
arr.forEach(el => {
count += +el;
})
return count;
}
console.log(sumArrNums(['1', '2', 5,5,5, '3']))
and yes, this is one of the few really solid use cases for reduce
:
function sumArrNums(arr) {
// NOTE: Assumes at least one entry! More below...
return arr.reduce((a, b) => +a + +b);
}
console.log(sumArrNums(['1', '2', 5,5,5, '3']))
Note there we're coercing both arguments to the callback, since on the first call they'll be the first two entries in the array (after that, the first argument will be the previously returned value, a number, but using +
on it is a no-op so it's fine).
That code assumes that arr
will have at least one entry. If it doesn't, reduce
will fail because if you don't provide an initial value for the accumulator and there aren't any elements in the array, it doesn't have any value to return. If you want to return 0
, the simplest thing is to provide the initial value, which also means you don't have to apply +
to the accumulator:
function sumArrNums(arr) {
return arr.reduce((acc, value) => acc + +value, 0);
}
console.log(sumArrNums(['1', '2', 5,5,5, '3']))
If you want to return something else (like NaN
) for the case where the array has no entries, you probably want to branch:
function sumArrNums(arr) {
return !arr.length ? NaN : arr.reduce((a, b) => +a + +b);
}
console.log(sumArrNums(['1', '2', 5,5,5, '3']))
Upvotes: 0
Reputation: 37755
You can use isNaN
to check if the number or string can be parsed to string or not, and than add values
Here +
before el
does implicit conversion from string to number
function sumArrNums(arr) {
let count = 0;
arr.forEach(el => count += !isNaN(el) ? +el : 0);
return count;
}
console.log(sumArrNums(['1', '2', 5, 5, 5, '3', {}, '1a', [] ]))
Upvotes: 2
Reputation: 1429
Use unary +
operator to convert your strings to numbers:
const sumArrNums = arr => arr.reduce((sum, num) => sum + +num, 0)
console.log(sumArrNums(['1', '2', 5,5,5, '3']))
Upvotes: 1