Skip to content
Advertisement

happy number algo question solution not working

trying to figure out this coding problem:

Write an algorithm to determine if a number n is “happy”.

A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.

Return True if n is a happy number, and False if not.

I’ve done some work but I’m not sure what I’m doing incorrectly. Would appreciate some pointers. Thanks!

function isHappy(numba1){
    let sum = 0;
    numba = numba1.toString().split('')
    let numbaArr = numba.map(y => parseInt(y))
    for (var x = 0; x< numbaArr.length; x++){
            sum += numbaArr[x] ** 2
    }
    if (sum > 1){
        isHappy(sum)
    }
    else if (sum === 1){
        return true
    }
    else if (sum <= 0){
        return false
    }
}

Advertisement

Answer

There are two problems I see with your answer, one small and one large.

  • Small: The value of the recursive call is not being returned. This:

    if (sum > 1){
        isHappy(sum)
    }
    

    should be

    if (sum > 1){
        return isHappy(sum)
    }
    
  • Large: you are not doing the essential work of checking whether we’re cycling over the same values. For instance in testing 15, we get these values

    15, 26, 40, 16, 37, 58, 89, 145, 42, 20, 4, 16
                ^^                              ^^
    

    and we can quit because we’ve seen 16 twice. 15 is not happy. But for 44 we get

    44, 32, 13, 10, 1
    

    and we hit 1 without cycling, so 44 is a happy number.

    Your code needs to keep track of the values it’s seen so far.

Here’s one recursive approach:

const digitsSquareSum = (n) => 
  String (n) .split ('') .map (n => n * n) .reduce ((a, b) => a + b, 0)

const _isHappy = (n, seen) =>
  n == 1
    ? true
  : seen .has (n) 
    ? false
  : _isHappy (digitsSquareSum (n), seen .add (n))

const isHappy = (n) => 
  _isHappy(n, new Set())

// display the happy numbers among the first 100 positive integers
console .log (Array .from ({length: 100}, (_, i) => i + 1) .filter (isHappy) .join(', '))

We use a helper function to calculate the sum of the squares of the digits. This simply makes the main function cleaner. The main function, _isHappy is an internal, private function, not to be exposed to the users. It is recursive and maintains a Set of the values we’ve already seen. If our number is 1, we return true. If our number is in the set of values we’ve already seen, we return false. Otherwise, we add it to the already seen set, calculate the next test case by calling our helper, and call _isHappy with those.

Our public function simply calls this main function, creating the initial empty Set of seen values, and passing that along with the number to test.

In our demo, we use Array .from ({length: 100}, (_, i) => i + 1), which is one of several compact ways of creating an array of integers from 1 to 100. In practice, I would abstract this into a range function that takes lo and hi values and creates an array of integers in between them, but that’s outside the point of this answer.

We do not have to use this breakdown of an internal recursive function with two parameters and a public function with one. We could use a default parameter like this:

const isHappy = (n, seen = new Set()) =>
  console .log({n, seen}) ||
  n == 1
    ? true
  : seen .has (n) 
    ? false
  : isHappy (digitsSquareSum (n), seen .add (n))

But there are some potential problems with this. For instance we could not call it like we did before:

range(1, 100) .filter (isHappy)

because filter supplies additional parameters to its callback. Not only does it supply the value but also the index and the whole array. However isHappy thinks the second parameter is the Set of seen values; when it gets passed the index, things will fail. We can do this instead:

range(1, 100) .filter ((n) => isHappy (n))

But we will always have to take such cautions when writing this way. I’ve gotten in the habit of doing this only for internal functions where I control how it’s called. And still once in a while it bites me.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement