FutureOwl
FutureOwl

Reputation: 13

Writing a Javascript function to find the average of digits of a number by recursion

I am trying to find the average the sum of digits of a number.

For example, for the number 123, the sum of digits of 123 is 6 and the number of digits in 123 is 3. So, the average of digits of 123 is 6/3 = 2.

I've only gotten as far as trying to find the sum through recursion unfortunately and often comes up as undefined. If I could figure this out I could find the average comfortably.

function averageOfDigits(number) {
//   Make the whole number into a string first to get the individual digits

  let arrOfStr = number.toString().split('');

//   Convert this array into integers
  let arrOfNum = arrOfStr.map(parseFloat)
  
// Find sum of these digits using recursion
  let sum = function sumRecursion (arrOfNum) {
    if (arrOfNum.length === 1) {
      return arrOfNum[0]
    } else {
      return arrOfNum.pop() + sum(arrOfNum)
    }
  } 
}
 

console.log(averageOfDigits(999))

Upvotes: 1

Views: 401

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50807

There are several interesting alternative recursive approaches. Our first one treats the number as a string and proceeds from there:

const _avgOfDigits = ([d, ...ds], total, count) =>
  d == undefined
    ? total / count
    : _avgOfDigits (ds, total + Number (d), count + 1)


const avgOfDigits = (n) => 
  _avgOfDigits (String (n) .split (''), 0, 0)

console .log (avgOfDigits (8675309)) //=> 5.428571428571429

Here we have a shell function that turns our number into an array of single-digit strings, then calls the private recursive function passing that array, and zeros for total and count. Our private function separates off the first digit and adds it to the total, increments the count, and recurs with these values and the remaining digits. When there are no more digits we return the quotient of the total and the digit count.


Our second one is more mathematical:

const avgOfDigits = (n, total = 0, count = 0) =>
  n == 0
    ? total / count
    : avgOfDigits (Math .floor (n / 10), total + n % 10, count + 1)

console .log (avgOfDigits (8675309)) //=> 5.428571428571429

Here we deal with the last digit and the remaining ones independently, turning, say, 8675309 into 9 and 867530, and using the 9 to increase our total, again incrementing our count, and recurring with 867530 and these new values. The recursion bottoms out the same way, and we return the same quotient.


A final sample is not recursive, showing an interesting running calculation for our average, not explicitly storing a total anywhere, and deriving the count from the running index:

const avgOfDigits = (n) => String (n) .split ('') .reduce (
  (avg, digit, idx) => (avg * idx + Number (digit)) / (idx + 1), 
  0
)

console .log (avgOfDigits (8675309)) //=> 5.428571428571429

Here we keep a running average, which we adjust using the index as a count of digits seen so far. The efficiency will suffer because on each iteration, we are not just adding two numbers but also performing a multiplication and a division. And it offers little over other simpler versions, but a variant of it could be used successfully with some sort of scan function.

Update

I didn't explain what I meant by that scan comment. The idea is simple enough. scan is a function that acts like reduce but keeps all the partially accumulated values. So scan ((a, b) => a + b)) (0) ([1, 2, 3, 4, 5]) //=> [1, 3, 6, 10, 15], which is [(1), (1 + 2), (1 + 2 + 3), (1 + 2 + 3 + 4), (1 + 2 + 3 + 4 + 5)].

It's easy enough to write a scan function, and with one of those, this style of averaging may become more useful. For example,

const scan = (fn) => (init) => (xs) => 
  xs .reduce ((a, x, i) => a .concat (fn (i == 0 ? init : a [i - 1], x, i)), [])

const digitAvgs = scan (
  (avg, digit, idx) => (avg * idx + Number (digit)) / (idx + 1), 
) (0)

console .log (digitAvgs ([8, 6, 7, 5, 3, 0, 9]))

Upvotes: 0

Bill the Lizard
Bill the Lizard

Reputation: 405955

You were close. Your implementation is setting sum equal to the recursive function, so that function is never getting called inside averageOfDigits. I think the confusing part was referring to the same function by two different names.

Here I define the sum function once, then call it twice. First is the internal recursive call, and second is in the return statement.

function averageOfDigits(number) {
//   Make the whole number into a string first to get the individual digits

  let arrOfStr = number.toString().split('');

  // Convert this array into integers
  let arrOfNum = arrOfStr.map(parseFloat)
  
  // Find sum of these digits using recursion
  function sum(arrOfNum) {
    if (arrOfNum.length === 1) {
      // base case reached
      return arrOfNum[0];
    } else {
      // return first digit + recursive call
      return arrOfNum.pop() + sum(arrOfNum);
    }
  }
  
  return sum(arrOfNum);
}
 
console.log(averageOfDigits(999))

You can finish off the averageOfDigits function by replacing the return statement with your own code. Right now it just returns the sum.

Upvotes: 2

Eric Fortis
Eric Fortis

Reputation: 17360

It's missing the initial call to the recursive function.

Hint:

  return (function sum(arr) {
    if (arr.length === 1) {
      return arr[0]
    } else {
      return arr.pop() + sum(arr)
    }
  }(arrOfNum))

Upvotes: 1

Related Questions