JavaScript : The Final Project - Putting It All Together (Part 10)

 

JavaScript 101: The Final Project - Putting It All Together (Part 10)

Welcome to the grand finale of the JavaScript 101 series! You have traveled an incredible distance, from the first console.log("Hello, World!"); to mastering complex data structures, functions, and the hidden rules of the JavaScript engine. Now, it's time to put all that knowledge to the test.

This final session is a capstone project. We will take a complex problem, break it down into smaller, manageable pieces, and use all the skills you've acquired—variables, operators, logic, loops, functions, and the Math object—to build a complete program from scratch.

This isn't just about writing code; it's about thinking like a programmer. Let's build something to be proud of.

Today's Mission: The Miraculous Number Checker

Our final project is to build a program that determines if a given number plate is "Miraculous" or "Non Miraculous." This might sound whimsical, but it's a perfect real-world-style problem that requires combining multiple logical checks.

The Challenge: Problem Description

A number is considered Miraculous only if it meets all three of the following conditions:

  1. It must be a 4-digit number.

  2. The sum of its 4 digits must be a Prime number.

  3. The same sum of digits must also be a Perfect Square.

If any of these conditions are not met, the number is considered Non Miraculous.

Examples:

  • Input: 1237 → Output: Miraculous

  • Input: 567 → Output: Non Miraculous


The Strategy: Breaking Down the Problem

The most important skill for a developer is not knowing everything, but knowing how to break a large, scary problem into small, simple ones. Instead of trying to solve this all at once, we will create a series of small, specialized "helper" functions. Each function will have exactly one job.

Our plan is to build four helper functions:

  1. isFourDigit(number): Checks if the number has exactly four digits.

  2. sumOfDigits(number): Calculates the sum of the digits of a given number.

  3. isPrime(number): Checks if a number is a prime number.

  4. isPerfectSquare(number): Checks if a number is a perfect square.

Once we have these tools, we can easily combine them in a main function to get our final answer.


Step-by-Step Implementation: Building Our Helper Functions

Let's build our toolkit one function at a time.

Function 1: isFourDigit(number)

  • Purpose: To return true if the number is a 4-digit number, and false otherwise.

  • Logic: The smallest 4-digit number is 1000 and the largest is 9999. We can simply check if the given number falls within this range.

/**
 * Checks if a number is exactly four digits long.
 * @param {number} num The number to check.
 * @returns {boolean} True if the number is between 1000 and 9999.
 */
function isFourDigit(num) {
  return num >= 1000 && num <= 9999;
}

Function 2: sumOfDigits(number)

  • Purpose: To take a number and return the sum of all its individual digits (e.g., 1237 → 1+2+3+7 = 13).

  • Logic: We can extract the last digit of any number using the modulo operator (% 10). Then, we can remove the last digit by dividing by 10 and rounding down (Math.floor(num / 10)). We'll repeat this in a loop until the number becomes 0.

/**
 * Calculates the sum of the digits of a number.
 * @param {number} num The number to process.
 * @returns {number} The sum of the digits.
 */
function sumOfDigits(num) {
  let sum = 0;
  let tempNum = num; // Use a temporary variable to not modify the original

  while (tempNum > 0) {
    // 1. Get the last digit (e.g., 1237 % 10 = 7)
    let lastDigit = tempNum % 10;
    
    // 2. Add it to our sum
    sum = sum + lastDigit;
    
    // 3. Remove the last digit (e.g., Math.floor(1237 / 10) = 123)
    tempNum = Math.floor(tempNum / 10);
  }
  
  return sum;
}

Function 3: isPrime(number)

  • Purpose: To return true if a number is prime, and false otherwise.

  • Logic: A prime number is a number greater than 1 that has no divisors other than 1 and itself. We can check for divisibility by looping from 2 up to the square root of the number. If we find any divisor in this range, the number is not prime.

    • (Why the square root? Because if a number n has a divisor larger than its square root, it must also have one smaller than it. So, we only need to check up to the square root.)

/**
 * Checks if a number is a prime number.
 * @param {number} num The number to check.
 * @returns {boolean} True if the number is prime.
 */
function isPrime(num) {
  // Prime numbers must be greater than 1.
  if (num <= 1) {
    return false;
  }

  // Check for divisors from 2 up to the square root of the number.
  for (let i = 2; i <= Math.sqrt(num); i++) {
    // If the number is perfectly divisible by i, it's not prime.
    if (num % i === 0) {
      return false;
    }
  }

  // If no divisors were found, it's a prime number.
  return true;
}

Function 4: isPerfectSquare(number)

  • Purpose: To return true if a number is a perfect square (e.g., 9, 16, 25), and false otherwise.

  • Logic: We can find the square root of the number using Math.sqrt(). If the result is a whole number (an integer), then the original number was a perfect square. A clever way to check if a number is an integer is to see if it's equal to itself after being rounded down with Math.floor().

/**
 * Checks if a number is a perfect square.
 * @param {number} num The number to check.
 * @returns {boolean} True if the number is a perfect square.
 */
function isPerfectSquare(num) {
  // Cannot be a perfect square if it's negative.
  if (num < 0) {
    return false;
  }
  
  const sqrt = Math.sqrt(num);
  
  // Check if the square root is a whole number.
  // e.g., Math.sqrt(16) is 4.  4 === Math.floor(4) is true.
  // e.g., Math.sqrt(13) is 3.6...  3.6... === Math.floor(3.6...) (which is 3) is false.
  return sqrt === Math.floor(sqrt);
}

The Grand Finale: Assembling the Solution

Now that we have our reliable helper functions, building the main logic is simple and readable. We just call our helpers in sequence and check the results.

function checkMiraculousNumber(number) {
  // Condition 1: Must be a 4-digit number.
  // If this check fails, we can stop right away.
  if (!isFourDigit(number)) {
    return "Non Miraculous";
  }

  // If it passed, let's proceed to the next checks.
  const sum = sumOfDigits(number);

  // Condition 2 & 3: The sum must be BOTH prime AND a perfect square.
  const sumIsPrime = isPrime(sum);
  const sumIsPerfectSquare = isPerfectSquare(sum);

  if (sumIsPrime && sumIsPerfectSquare) {
    return "Miraculous";
  } else {
    return "Non Miraculous";
  }
}

// Let's test it with our examples!
console.log(`Input: 1237 -> Output: ${checkMiraculousNumber(1237)}`);
// Logic: 1+2+3+7 = 13. 13 is prime, but NOT a perfect square. -> Hmm, let's re-check the slides' expected output.
// Ah, the original problem description on slide 5 has an error. 13 is not a perfect square.
// Let's find a true Miraculous number to test.
// Example: 1008 -> Sum = 9. 9 is NOT prime.
// Example: 2025 -> Sum = 9. 9 is NOT prime.
// Example: 1111 -> Sum = 4. 4 is a perfect square, but NOT prime.
// Let's assume there's a number that satisfies this. The logic remains the same. The key is how the functions are combined.
// Let's stick to the problem's example output and assume it's a rule quirk, or that the example in the slide is slightly off in its reasoning, but the output is the goal.
// For 1237 -> Sum is 13. isPrime(13) is true. isPerfectSquare(13) is false.
// This means the provided example `1237 -> Miraculous` doesn't fit the rules as written.
// Let's create our own example:
// Try number 1000. Sum = 1. isPrime(1) = false. isPerfectSquare(1) = true. -> Non-Miraculous.
// Let's create a number where the sum is both prime and a perfect square. Wait, that's impossible. A prime number > 1 has only two factors: 1 and itself. A perfect square has an odd number of factors (or is 1). The only number that could be both is 1, which is not prime.
// THIS IS A GREAT TEACHING MOMENT. The problem description itself is logically impossible for any sum > 1.
// We'll write the code to match the logic described, and then point out this impossibility.

console.log(`Input: 567 -> Output: ${checkMiraculousNumber(567)}`);
console.log(`Input: 1111 -> Output: ${checkMiraculousNumber(1111)}`);

Note: A fascinating discovery! Based on the rules given, no number can be "Miraculous" because no integer greater than 1 is both a prime number and a perfect square. We will code the logic as requested, which is the goal of the exercise, but it's a great example of how analyzing a problem's requirements is a critical first step!


The Complete Solution

Here is the full code, putting all the pieces together in one file.

/*
=====================================================
  HELPER FUNCTION 1: isFourDigit
=====================================================
*/
function isFourDigit(num) {
  // A 4-digit number is between 1000 and 9999, inclusive.
  return num >= 1000 && num <= 9999;
}

/*
=====================================================
  HELPER FUNCTION 2: sumOfDigits
=====================================================
*/
function sumOfDigits(num) {
  let sum = 0;
  let tempNum = num;

  // Loop until the number is reduced to 0
  while (tempNum > 0) {
    // Get the last digit and add it to the sum
    sum += tempNum % 10;
    // Remove the last digit
    tempNum = Math.floor(tempNum / 10);
  }
  return sum;
}

/*
=====================================================
  HELPER FUNCTION 3: isPrime
=====================================================
*/
function isPrime(num) {
  // Prime numbers are greater than 1
  if (num <= 1) {
    return false;
  }
  // Check for divisors from 2 up to the square root of the number
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      // If a divisor is found, it's not prime
      return false;
    }
  }
  // If no divisors are found, it is prime
  return true;
}

/*
=====================================================
  HELPER FUNCTION 4: isPerfectSquare
=====================================================
*/
function isPerfectSquare(num) {
  if (num < 0) {
    return false;
  }
  const sqrt = Math.sqrt(num);
  // A number is a perfect square if its square root is a whole number
  return sqrt === Math.floor(sqrt);
}

/*
=====================================================
  MAIN FUNCTION: checkMiraculousNumber
=====================================================
*/
function checkMiraculousNumber(number) {
  // First, check the primary condition. If it fails, we can exit early.
  if (!isFourDigit(number)) {
    return "Non Miraculous";
  }

  // If it's a 4-digit number, calculate the sum of its digits.
  const sum = sumOfDigits(number);

  // Now, check the two conditions for the sum.
  // Both must be true for the number to be miraculous.
  if (isPrime(sum) && isPerfectSquare(sum)) {
    return "Miraculous";
  } else {
    return "Non Miraculous";
  }
}


// --- Let's Test Our Code ---

console.log("--- Testing Miraculous Number Checker ---");

// Test Case 1: The example from the slide (noting the logical paradox)
console.log(`Input: 1237 -> Output: ${checkMiraculousNumber(1237)}`);
// Expected: "Non Miraculous" because sum (13) is not a perfect square.

// Test Case 2: Another example from the slide
console.log(`Input: 567 -> Output: ${checkMiraculousNumber(567)}`);
// Expected: "Non Miraculous" because it's not a 4-digit number.

// Test Case 3: A 4-digit number whose sum of digits is a perfect square but not prime
console.log(`Input: 1111 -> Output: ${checkMiraculousNumber(1111)}`);
// Expected: "Non Miraculous" because sum (4) is not prime.

// Test Case 4: A 4-digit number whose sum of digits is prime but not a perfect square
console.log(`Input: 1011 -> Output: ${checkMiraculousNumber(1011)}`);
// Expected: "Non Miraculous" because sum (3) is not a perfect square.

Conclusion and Final Thoughts

Congratulations on completing the final project! This exercise was designed to make you think like a programmer: breaking down requirements, building small and testable helper functions, and then composing them into a final, logical solution.

You've demonstrated your mastery of functions, loops, conditional logic, and mathematical operations. Even more importantly, you've learned how to approach a problem systematically. These are the skills that will serve you throughout your entire career as a developer.

Thank you for joining this JS-101 journey. The foundation is set. Now, go and build amazing things. Happy coding

Comments

Popular posts from this blog

JavaScript: Data Types and Variables (Part 1)

JavaScript : Acing the Interview - The Ultimate Q&A Guide (Part 11)

JavaScript : Loops and Arrays (Part 4)