Start learning free

LESSON 249 OF 675 · HELPER FUNCTIONS

Perfect Numbers in TypeScript: A Divisor-Sum Helper

Practice Read the full explanation

You get an array of integers nums. Return the perfect numbers among them, in the same order.

A number is perfect when it equals the sum of its divisors smaller than itself. For example, 6 is perfect, because 1 + 2 + 3 = 6. The number 1 is not perfect. It has no smaller divisors, so its sum is 0.

If a perfect number appears twice, it is kept twice. If no number is perfect, the result is empty.

The work is split between two functions. The first one is a helper. It gets one number and returns the sum of its divisors smaller than itself. The second one is the function that is graded. It keeps every number that equals the sum the helper returns for it.

 

Example 1:

Input: nums = [6, 8, 28, 12]
Output: [6, 28]
Explanation: 6 = 1 + 2 + 3.
28 = 1 + 2 + 4 + 7 + 14.
For 8, the sum is 1 + 2 + 4 = 7.
For 12, the sum is 1 + 2 + 3 + 4 + 6 = 16.

Example 2:

Input: nums = [1, 5, 10]
Output: []
Explanation: The sum for 1 is 0.
The sum for 5 is 1.
The sum for 10 is 1 + 2 + 5 = 8.

Example 3:

Input: nums = [496, 6, 6]
Output: [496, 6, 6]
Explanation: 496 is perfect too.
6 appears twice, so it is kept twice.

 

Constraints:

  • nums has 1 to 20 numbers.
  • Every number is between 1 and 10,000.
STEP 1 OF 8
    Ctrl + Enter to check

    TYPESCRIPT · HELPER FUNCTIONS

    Perfect Numbers in TypeScript, explained

    Problem overview

    The method perfectNumbers receives an array of integers nums and returns the perfect numbers among them, in the same order. A number is perfect when it equals the sum of its divisors that are smaller than itself. For 6, those divisors are 1, 2 and 3, and 1 + 2 + 3 = 6, so 6 is perfect. The number 1 is not perfect: it has no smaller divisors, so its sum is 0. A perfect number that appears twice is kept twice, and if no number is perfect the result is empty. The array has 1 to 20 numbers, each from 1 to 10,000.

    For [6, 8, 28, 12] the answer is [6, 28]. The sum for 8 is 1 + 2 + 4 = 7, and for 12 it is 1 + 2 + 3 + 4 + 6 = 16, so neither matches. For [1, 5, 10] the sums are 0, 1 and 8, so the answer is []. For [496, 6, 6] every number is perfect, so all three stay.

    Approach: one helper, one comparison

    The lesson splits the job in two. The helper sumOfDivisors takes a single number n and returns the sum of the divisors of n that are smaller than n. The graded method perfectNumbers walks through the array and keeps every number that equals the helper's answer for it. The definition of a perfect number becomes a single comparison: this.sumOfDivisors(num) === num.

    Inside the helper, try every candidate divisor d from 1 up to n - 1. A number d divides n exactly when n % d is 0, so each such d is added to a running total. Stopping at n - 1, with d < n, leaves n itself out, which is what "smaller than itself" asks for.

    Step by step

    1. In sumOfDivisors, declare total as 0. For n = 1 the loop never runs, and 0 is the right answer.
    2. Loop d from 1 while d < n. The condition is what excludes n itself.
    3. If n % d === 0, add d to total. A remainder of 0 means d divides n.
    4. Return total. For 6 it is 6, for 8 it is 7.
    5. In perfectNumbers, create perfect, an empty number[].
    6. Loop over nums with for (const num of nums). A number that appears twice is visited twice.
    7. Compare this.sumOfDivisors(num) with num, and push num when they are equal.
    8. Return perfect after the loop. It stays empty if no number matched.

    The solution in TypeScript

    class Solution {
        sumOfDivisors(n: number): number {
            let total: number = 0;
            for (let d = 1; d < n; d++) {
                if (n % d === 0) {
                    total += d;
                }
            }
            return total;
        }
    
        perfectNumbers(nums: number[]): number[] {
            let perfect: number[] = [];
            for (const num of nums) {
                if (this.sumOfDivisors(num) === num) {
                    perfect.push(num);
                }
            }
            return perfect;
        }
    }
    

    Points worth knowing:

    • % is the remainder operator. 10 % 3 is 1 and 6 % 3 is 0. Every number gives remainder 0 when divided by 1, so 1 is always counted for n > 1.
    • === compares numbers by value. Both sides are numbers here, so it does what you expect.
    • Calling the helper needs this.. Without it the compiler reports that the name cannot be found.
    • The helper is called once per number. It does not remember earlier answers, and with at most 20 numbers up to 10,000 that is fast.

    Tracing the helper on 12

    For n = 12, d runs from 1 to 11. The remainder 12 % d is 0 for d = 1, 2, 3, 4 and 6, so those five values are added: 1, then 3, then 6, then 10, then 16. The other candidates, 5, 7, 8, 9, 10 and 11, leave a remainder and add nothing. The helper returns 16, which is not 12, so 12 is not perfect. A number whose divisor sum is bigger than itself, like 12, is not perfect any more than one whose sum is smaller, like 8.

    Divisor sums of the examples

    Calling the helper gives sumOfDivisors(6) = 6, sumOfDivisors(8) = 7, sumOfDivisors(12) = 16, sumOfDivisors(28) = 28, sumOfDivisors(1) = 0, sumOfDivisors(5) = 1 and sumOfDivisors(10) = 8. A sum equal to the number marks it as perfect, and 6 and 28 are the only ones in the first example. Within the range 1 to 10,000 the perfect numbers are 6, 28, 496 and 8128, and perfectNumbers([8128, 10000]) returns [8128].

    Checking fewer candidates

    No divisor of n other than n itself is bigger than n / 2, so the loop could stop there:

    sumOfDivisors(n: number): number {
        let total: number = 0;
        for (let d = 1; d <= n / 2; d++) {
            if (n % d === 0) {
                total += d;
            }
        }
        return total;
    }
    

    It returns the same sums: 6 for 6, 28 for 28, 0 for 1 and 14211 for 10000. The lesson's version tests every number below n, which is simpler to reason about and fast enough for inputs up to 10,000.

    Edge cases and common mistakes

    • Including n itself. With d <= n the sum for 6 becomes 12, not 6, and no number would ever equal its sum.
    • Starting the loop at 0. n % 0 is NaN in JavaScript, not an error, so it adds nothing here, but 0 is not a divisor. The right start is 1.
    • The number 1. Its sum is 0, not 1, so it is not perfect. The empty loop gives that for free.
    • Returning inside the loop. A return inside the for would stop at the first number.
    • Dropping duplicates. The task keeps a perfect number every time it appears, so there is no set or includes check.

    Testing

    • [6, 8, 28, 12] gives [6, 28].
    • [1, 5, 10] gives [].
    • [496, 6, 6] gives [496, 6, 6].
    Keep going with TypeScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free