Lucky numbers in JS!

·

5 min read

What is a lucky number?

So we are going to create some code which I used as a challenge to the members of a Discord server I'm in. The challenge had the following criteria:

  • A lucky number is defined as any number that has a '6' or an '8' in it. A number with neither of these numbers, or both, is unlucky.

  • Create a program that, given two numbers L and R, works out the number of lucky numbers between those two values

  • The tough version, is to do this without strings

Calculating, with strings

So, the code to do this by using strings is pretty straightforward, as you might have guessed:

function countLuckyNumbers(L, R) {
  let count = 0;
  for (let i = L; i <= R; i++) {
    let numberString = i.toString();
    if (numberString.includes("6") && !numberString.includes("8")) {
      count++;
    } else if (numberString.includes("8") && !numberString.includes("6")) {
      count++;
    }
  }
  return count;
}

This code:

  • Sets a count to 0

  • Turns the number to a string with the toString() method

  • Uses the includes() method to see if the string either includes 6 and not 8, or 8 and not 6, and will increase the counter by 1 if it sees this

  • Because we are not counting up for 6 and 8 or neither, it will not count these numbers

  • Return the count

For anyone with experience of JS, this should be fairly straightforward, assuming you know the methods to use

Calculating, without strings

This however is harder. We are effectively looking at the number still, but because we cannot use strings, we must think a little harder.

Here is the code I came up with, and I will explain it:

function countLuckyNumbers(L, R) {
  let count = 0;
  for (let i = L; i <= R; i++) {
    let hasSix = false;
    let hasEight = false;
    let num = i;

    // Extract each digit of the number and check if it is 6 or 8
    while (num > 0) {
      let digit = num % 10;
      if (digit === 6) {
        hasSix = true;
      } else if (digit === 8) {
        hasEight = true;
      }
      num = Math.floor(num / 10);
    }

    // Check if the number is a lucky number and increment the count if it is
    if (hasSix && !hasEight || hasEight && !hasSix) {
      count++;
    }
  }

  return count;
}

Some variable initialisation at the start, but nothing fancy.

let hasSix = false;
let hasEight = false;
let num = i;

Now for the clever part:

while (num > 0) {
      let digit = num % 10;
      if (digit === 6) {
        hasSix = true;
      } else if (digit === 8) {
        hasEight = true;
      }
      num = Math.floor(num / 10);
    }

What I'm doing here is letting the digit, or the right most value, be the one I look at.

If this is a 6, then I set hasSix to true, and the same for hasEight.

I then, at the end, use the Math.floor() method to get rid of the digit.

Here is a better explanation of what is happening at each step:

An unlucky number

// let's use 567 and 568 as an example, 568 first
while(num > 0) { // num is 568 so this is false
    let digit = num % 10
    // 568 % 10 = 8, because the % operator gives the remainder of any number left when dividing these 2 numbers by 10
    ...
    if(digit === 8) {
        hasEight = true;
    }
    // because the digit is 8, we have set the hasEight boolean to true
    num = Math.floor(num / 10);
    // 568 / 10 = 56.8, and the floor() method will get rid of the decimal and just give us 56, so now we are going through the loop with 56
while(num > 0) { // num is 56 so this is false
    let digit = num % 10
    // 56 % 10 = 6
    ...
    if(digit === 6) {
        hasSix = true;
    }
    // digit is 6 so this will set the hasSix boolean to true
    num = Math.floor(num / 10);
    // 56 / 10 = 5.6 which just resolves to 5 with the floor() method

    // and now I hope you can see the last time round, it'll be 5, which won't satisfy either argument, and then when it gets to the end, the floor() method will make num = 0, which will break the loop. we now move onto the next part:

    if (hasSix && !hasEight || hasEight && !hasSix) {
      count++;
    }
    // hasSix and hasEight are true, which means this will return false, and won't increase the count, making it an UNLUCKY number

A lucky number

while(num > 0) { // num is 567 so this is false
    let digit = num % 10
    // 567 % 10 = 7, because the % operator gives the remainder of any number left when dividing these 2 numbers by 10
    // because the digit is 7, this is false and will skip both checking for 6 and 8
    num = Math.floor(num / 10);
    // 567 / 10 = 56.7, and the floor() method will get rid of the decimal and just give us 56, so now we are going through the loop with 56
while(num > 0) { // num is 56 so this is false
    let digit = num % 10
    // 56 % 10 = 6
    ...
    if(digit === 6) {
        hasSix = true;
    }
    // digit is 6 so this is of course, true
    num = Math.floor(num / 10);
    // 56 / 10 = 5.6 which just resolves to 5 with the floor() method

    // and now I hope you can see the last time round, it'll be 5, which won't satisfy either argument, and then when it gets to the end, the floor() method will make num = 0, which will break the loop. we now move onto the next part:

    if (hasSix && !hasEight || hasEight && !hasSix) {
      count++;
    }
    // hasSix is true, but hasEight is now false, so this will add 1 number to the count

In summary

I hope this explained it to those who struggled with the challenge. Remember, we learn when we struggle, as long as we learn where we went wrong, record our mistakes and move forward.

I hope you enjoyed this look into how to solve a problem in an unconventional way, and please comment if you need some help with any part of this!

Appreciate each and every one of you who stops by to read my goings on! ❤️