9
\$\begingroup\$

I have implemented the "Sum All Numbers in a Range" challenge from Free Code Camp quoted below.

The code works like a charm, but I don't think it's idiomatic. The challenge hints imply I should be using Math.max(), Math.min() and Array.reduce(). In addition I used the spread operator from ES6 to simplify both Math calls.

I'm looking for a more idiomatic way to solve this. I think it can be done in a one-liner, something like:

return arr.sort((function(a, b) { return a - b; })).reduce(function(a, b) { /* */ });

But I'm pretty sure I'm heading the wrong way with the above.

The challenge:

We'll pass you an array of two numbers. Return the sum of those two numbers and all numbers between them.

The lowest number will not always come first.

The code:

function sumAll(arr) {
  var out = 0;
  for (var i = Math.min(...arr); i <= Math.max(...arr); i++) {
    out += i;
  }
  return out;
}

Test cases:

sumAll([1, 4])
sumAll([4, 1])
sumAll([5, 10])
sumAll([10, 5])

Expected output:

10
10
45
45
\$\endgroup\$
  • \$\begingroup\$ How do you get freecodecamp to use es6? \$\endgroup\$ – Barry Jan 18 '16 at 16:56
  • \$\begingroup\$ @Barry It's not allowed for every challenge for some reason, but on this one it just gave a notification ES6 was required. Nothing else. \$\endgroup\$ – Mast Jan 18 '16 at 17:11
  • 2
    \$\begingroup\$ ever heard of Gauss? (en.wikipedia.org/wiki/Carl_Friedrich_Gauss#Anecdotes) \$\endgroup\$ – njzk2 Jan 18 '16 at 21:53
  • 2
    \$\begingroup\$ the oneliner you give is very close. in the final function you have a and b being the min and max values. just apply gauss to it: return (a+b)*(b-a+1)/2; \$\endgroup\$ – njzk2 Jan 18 '16 at 21:58
19
\$\begingroup\$

I don't know what you would use reduce() for to be honest. But you don't need to loop. We just have a simple arithmetic sequence with a straightforward formula:

$$\sum_{i=min}^{max} i = \frac{(max-min+1)(min+max)}2$$

We just need to pick out which one is larger:

function sumFrom(min, max) {
    return (max-min+1) * (min+max) / 2;
}

function sumAll(arr) {
    return sumFrom(Math.min(...arr), Math.max(...arr));
}

Or we could generalize to any arithmetic sequence:

function sumArithmetic(a1, n, d) {
    return n*(2*a1 + (n-1)*d) / 2;
}

function sumAll(arr) {
    var min = Math.min(...arr);
    return sumArithmetic(min, Math.max(...arr) - min + 1, 1);
}

I guess if you really want reduce() you'd do it this way (not using ES6 because I don't know how to get freecodecamp to use it):

function sumAll(arr) {
  var min = Math.min.apply(null, arr);
  var max = Math.max.apply(null, arr);

  return Array.apply(null, Array(max-min+1))   // get array of correct size
      .map(function(_, b) { return b+min; })   // change it to have correct values
      .reduce(function(a, b) { return a+b; }); // and sum it
}
\$\endgroup\$
  • 1
    \$\begingroup\$ you would use reduce because it is a way to not access the array by indexes. \$\endgroup\$ – njzk2 Jan 18 '16 at 21:54
  • \$\begingroup\$ $max(a,b)-min(a,b)=|a-b|$, $min+max=a+b$ saves you some steps. \$\endgroup\$ – A.S. Jan 19 '16 at 1:09
3
\$\begingroup\$

Like Barry mentioned, you are probably expected to use Gauss and the property of triangular numbers.

reduce is a funny way of accessing your array without touching the indexes, hence without needing to verify that they exist. (consider what would happen if your function was called with [] or [0]).

The one-liner you give is very close. In the final function you have a and b being the min and max values in that order. just apply the sum formula to it: return (a+b)*(b-a+1)/2;

So:

return arr
        .sort((a, b) => a - b)
        .reduce((a, b) => (a + b) * (b - a + 1) / 2);

You'll notice that since the array is size 2 and known to be that way, the complexity is O(1), while if you actually sum the numbers, it becomes O(n) where n is the amount of numbers to add.

\$\endgroup\$
2
\$\begingroup\$

Personally, I'd change out to result. And also the structure in something like the following:

function sumAll(arr){
    var smallest = 0;
    var greatest = 0;

    if(arr[0] < arr[1]){
        smallest = arr[0];
        greatest = arr[1];
    } else{
        smallest = arr[1];
        greatest = arr[0];
    }

    return sumRange(smallest, greatest);
}

function sumRange(from, to){
    var result = 0;

    for(var i = from; i <= to; i++){
        result += i;
    }

    return result;
}

In this way it becomes more readable IMO.

\$\endgroup\$
  • 2
    \$\begingroup\$ Javascript does not support ad hoc polymorphism. only the second function exists. \$\endgroup\$ – njzk2 Jan 18 '16 at 22:09
  • \$\begingroup\$ @njzk2 thanks for pointing that out. Silly mistake. Now it should be ok. \$\endgroup\$ – Gentian Kasa Jan 18 '16 at 22:48

protected by Jamal Apr 23 '17 at 20:48

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.