Andrey Bondarev
Andrey Bondarev

Reputation: 119

Function that sums array numbers (including numbers as strings)

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

Answers (5)

georg
georg

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.

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 NaNs

Number conversions can fail, and when they fail, they return a NaN and NaNs 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)

Upvotes: 2

Djaouad
Djaouad

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

T.J. Crowder
T.J. Crowder

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

Code Maniac
Code Maniac

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

Marcos Luis Delgado
Marcos Luis Delgado

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

Related Questions