1

Just working on a project and tried a few different solutions but with no results. Could someone help me with how to add up numbers in a nested array? Would I use reduce? or a for loop?

function balance(arr) {
  if(typeof item == 'number') {
    return arr;enter code here
  } else {
    return arr + balance(item);
  }
}
7
  • show you array and your attempted code Commented Feb 26, 2016 at 5:50
  • 2
    Recursively reduce, by the sound of it. No results for any of your attempted solutions, though? Commented Feb 26, 2016 at 5:50
  • 1
    reduce can help with modular code, but a for loop likely is faster (and not much more code). Avoid recursion if you can, it's slow. Commented Feb 26, 2016 at 5:55
  • 2
    @RobG: Unless the depth of nesting is known, there's no way to avoid recursion here, though, is there? And unless it is known that we're dealing with unreasonable amounts of data, I wouldn't trade readability for speed of execution. Commented Feb 26, 2016 at 6:00
  • 1
    @ArnoldB I think you may have left out a reduce function and array Commented Feb 26, 2016 at 6:04

4 Answers 4

2

Is this maybe what you are hoping for?

function balance(arr) {
  return arr.reduce(function(sum, item) {
    if(typeof item == 'number') {
      return sum;
    } else {
      return sum + balance(item);
    }
  },0);
}

console.log(balance([1,2,[3,4],5]));
Sign up to request clarification or add additional context in comments.

1 Comment

This doesn't work, you need sum += item before the line return sum;.
2

Just for the record (to disprove the assertion that recursion is required), here's a version that uses a sequential algorithm. Recursion is concise and (usually) easier to read, however if speed matters, it can be slow. However, based on results from jsPerf, script engines seem very much better at optimising recursive code than they used to be, at least for simple programs like this.

For comparison, I've included a recursive version using a plain loop, the jsPerf tests also include a (fixed) recursive version using reduce. I suspect Any's answer will be slowest as it calls slice and itself on every loop, but I didn't have time to fix it.

So I guess recursion is fine here as it is fast and concise.

/*  Sum the values of nested arrays. Only checks if values are arrays,
**  otherwise assumes they're numbers
**
**  @param {Array} arr - array of numbers to process, may have 
**                       nested arrays of numbers
**  @returns {number} - sum of values or NaN if arr is not an Array
*/
function balance(arr) {
  
  // Only process arrays
  var isArray = Array.isArray;
  if (!isArray(arr)) return NaN;

  // Setup
  var arrays = [], indexes = [];
  var currentArray = arr;
  var currentValue;
  var sum = 0;
  var i = 0, iLen = arr.length;
  
  // Use <= length as must handle end of array inside loop
  while (i <= iLen || arrays.length) {
    currentValue = currentArray[i];

    // If at end of current array, reset value to before entering array
    // Reset i to previous value as will increment at the bottom
    if (i == currentArray.length && arrays.length) {
      currentArray = arrays.pop();
      i = indexes.pop();
      iLen = currentArray.length;

    // If current value is an array, use it and reset loop control values
    // set i to -1 as will increment at the bottom
    } else if (isArray(currentValue)) {
      arrays.push(currentArray);
      indexes.push(i);
      currentArray = currentValue;
      i = -1;
      iLen = currentArray.length;

    // Otherwise, add the current value
    // Will be undefined if at end of array so add zero
    } else {
      sum += +currentValue || 0;
    }
    
    // Increment i
    i++;
  }
  return sum;
}

document.write(
  'balance sequential 1: ' +
  balance([1,[2,1,[1,2,-1],[1]],1,[2,1]]) // 11
  + '<br>balance sequential 2: ' +
  balance([1,2,[3,4],5]) // 15
);


/*  Sum the values of nested arrays. Only checks if values are arrays,
**  otherwise assumes they're numbers
**
**  @param {Array} arr - array of numbers to process, may have 
**                       nested arrays of numbers
**  @returns {number} - sum of values or NaN if arr is not an Array
*/
function balanceLoop(arr) {
  if (!Array.isArray(arr)) return NaN;
  for (var value, total=0, i=0, iLen=arr.length; i<iLen; i++) {
    value = arr[i];
    total += Array.isArray(value)? balanceLoop(value) : value;
  }
  return total;
}

document.write(
  '<br>balanceLoop 1: ' +
  balanceLoop([1,[2,1,[1,2,-1],[1]],1,[2,1]]) // 11
  + '<br>balanceLoop 2: ' +
  balanceLoop([1,2,[3,4],5]) // 15
);

Comments

1

A simple recursive function:

function balance(arr, total) {
  total = total || 0;
  if (arr.length === 0) return total;
  var head = arr[0];
  if (typeof head === 'number') {
    return balance(arr.slice(1), total += head);
  } else {
    return balance(head, total);
  }
}

balance([1, [2, 1, 3, [43, 2]]])); // 52

DEMO

3 Comments

Does not work with arrays nested like [1,[2],1], it returns 3 instead of 4. It's also inefficient to call itself on every member of the array rather than just when encountering a new array member.
:) I was about to comment about you being needlessly combative in your comment, but now I don't have to. Thanks for picking up on the error, and pointing me in the right direction. Recursion still causes me a few problems, but I'll get there in the end.
It's a challenge to ensure written communication remains friendly without excessive use of emoticons or being overly cautious (which can be annoying in itself). Please accept comments as well meant, not argumentative. :-)
0

I would probably solve this using a recursive reduce, in the following manner:

function balance(arr) {
  return arr.reduce(function(sum,item) { 
    return sum + (item instanceof Array ? balance(item) : item);
  }, 0); 
};

balance([1,[2,1,[1,2,-1],[1]],1,[2,1]]); // 11

If you don't mind the overhead, you could of course do this:

Number.prototype.balance = function() { return this; };
Array.prototype.balance = function() { return this.reduce(function(a,b) { return a + b.balance(); }, 0); }

[1,[2,1,[1,2,-1],[1]],1,[2,1]].balance(); // 11

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.