0

I have a function sum([1,2,3,4],50,10,[10, 20],1), consisting of both integers and arrays and I want to return the sum of all single elements. In the given example the sum would be 101.

Question: I found a way that works, but I'd like to know, what I could do to make it more efficient.

My approach works as follows:

  1. intialize a new array; those arguments that are arrays, like [1,2,3,4], will be turned into a sum first and then pushed into the new array.
  2. for-loop through all arguments
  3. check, if argument is an array
    • if yes, sum all elements up (via .reduce()) and push sum into new array.
    • if no, push element into new array
  4. Turn newArray into sum (again via .reduce()) and return.

Code:

console.log("Result: " + sum([1,2,3,4],50,10,[10, 20],1));

function sum(...allArgs) {

    let newArray = [];
    // go through all args
    allArgs.forEach((elem) => {
        // check if argument is an array
        if (Array.isArray(elem)){
            // arraySum = all Items of array 
            arraySum = elem.reduce((acc, currV) => acc + currV, 0);
            // push arraySum into new Array
            return newArray.push(arraySum);
        }
        // push other arguments (that are not arrays) into new array
        newArray.push(elem);
    });
    // return the sum of the new array
    return newArray.reduce((acc, currV) => acc + currV);
}

I found a comparable post, with the difference that in that post no arguments were arrays, hence this post.

What could I improve in this procedure?

Josh
  • 125
  • 7

4 Answers4

2

you can replace forEach with reduce

function sum(...allArgs) {

  // go through all args
  return allArgs.reduce((sum, elem) => {
      // check if argument is an array
      if (Array.isArray(elem)){
          // arraySum = all Items of array 
          return sum + elem.reduce((s,i)=>s+i,0);
      }else{
        return sum + elem;
      }
  },0);
}

sum([1,2,3],50,10,[10, 20],1)

or nested sum

function sum(...allArgs) {
  // go through all args
  return allArgs.reduce((total, elem) => {
    // check if argument is an array
    if (Array.isArray(elem)) {
      return total + sum(...elem);
    } else {
      return total + elem;
    }
  }, 0);
}

sum([1, 2, 3], 50, 10, [10, 20,[11,12]], 1);
Gabriel Tong
  • 206
  • 3
  • 12
2

Another way you might like -

const sum = (x, ...more) =>
  x === undefined
    ? 0                       // 1
    : Array.isArray(x)
        ? sum(...x, ...more)  // 2
        : x + sum(...more)    // 3

console.log(sum([1,2,3,4],50,10,[10, 20],1)) // 101
  1. base case: x is undefined – terminate the computation and return the empty sum, 0
  2. inductive case: x is not undefined and x is an Array – Return the sum the spread of x and more
  3. inductive case: x is not undefined and x is not an Array – x is therefore a single element. Return x plus the sum of more.

Pencil & Paper evaluation -

The numbered comment at the end of the line corresponds with the numbered code-paths above

sum([1,2,3,4],50,10,[10,20],1)                                  // 2
= sum(1,2,3,4,50,10,[10,20],1)                                  // 3
= 1 + sum(2,3,4,50,10,[10,20],1)                                // 3
= 1 + (2 + sum(3,4,50,10,[10,20],1))                            // 3
= 1 + (2 + (3 + sum(4,50,10,[10,20],1)))                        // 3
= 1 + (2 + (3 + (4 + sum(50,10,[10,20],1))))                    // 3
= 1 + (2 + (3 + (4 + (50 + sum(10,[10,20],1)))))                // 3
= 1 + (2 + (3 + (4 + (50 + (10 + sum([10,20],1))))))            // 2
= 1 + (2 + (3 + (4 + (50 + (10 + sum(10,20,1))))))              // 3
= 1 + (2 + (3 + (4 + (50 + (10 + (10 + sum(20,1)))))))          // 3
= 1 + (2 + (3 + (4 + (50 + (10 + (10 + (20 + sum(1))))))))      // 3
= 1 + (2 + (3 + (4 + (50 + (10 + (10 + (20 + (1 + sum())))))))) // 1
= 1 + (2 + (3 + (4 + (50 + (10 + (10 + (20 + (1 + 0))))))))  
= 1 + (2 + (3 + (4 + (50 + (10 + (10 + (20 + 1)))))))
= 1 + (2 + (3 + (4 + (50 + (10 + (10 + 21))))))
= 1 + (2 + (3 + (4 + (50 + (10 + 31)))))
= 1 + (2 + (3 + (4 + (50 + 41))))
= 1 + (2 + (3 + (4 + 91)))
= 1 + (2 + (3 + 95))
= 1 + (2 + 98)
= 1 + 100
= 101
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Looks interesting! I don't understand how the callbackFn works itself through the list of arguments though, because my rookie-eyes see no iterative structure. Could you briefly explain the steps? – Josh May 01 '19 at 12:43
  • 1
    callback function? it's a simple arrow function with _recursive_ structure, which is why you don't see iterative steps or other imperative-style statements. I added some notes, but running `sum` with pencil and paper is all that should be needed to see how it works – Mulan May 01 '19 at 17:27
  • You most likely have extra folds in your prefrontal cortex to be able to do that. – King Friday May 08 '19 at 03:42
  • 1
    Nice to hear from you again, Jason. I hope you are doing well :D – Mulan May 08 '19 at 20:22
1

You could also use a good old fashion for loop... similar to Gabriel Tongs answer.

function sum(...allArgs) {
  var total = 0;
  for(var i = 0; i < allArgs.length; i++){
    if(Array.isArray(allArgs[i])) {
      total += sum(...allArgs[i]) 
    } else {
      total += allArgs[i];
    }
  }
  return total;
}
Julian
  • 500
  • 2
  • 10
1

Array.flat, and reduce is all you really need.

function sum(...allArgs) {
  return allArgs.flat().reduce((a, v) => a + v);
}

console.log("Result: " + sum([1,2,3,4],50,10,[10, 20],1));
Keith
  • 22,005
  • 2
  • 27
  • 44
  • the goal of the question was efficiency. The flat method is going to loop through the original array once then run the reduce. I'm pretty sure that (although much cleaner) this is just as efficient as the op. – Julian May 01 '19 at 00:39
  • @Julian Yes, if speed is important, this won't be the fastest option. To be honest, this question might be best served in code review. https://codereview.stackexchange.com/ – Keith May 01 '19 at 01:01