javascript

exercises

exercises.js
/**
 * =====================================================
 * 5.8 RECURSION - EXERCISES
 * =====================================================
 * Practice recursive problem solving
 */

/**
 * Exercise 1: Countdown
 *
 * Write a recursive function that counts down from n to 1,
 * then prints "Blastoff!"
 */
console.log('Exercise 1: Countdown');

function countdown(n) {
  // TODO: Implement recursive countdown
}

// Test:
// countdown(5);
// Should print: 5 4 3 2 1 Blastoff!

/**
 * Exercise 2: Sum to N
 *
 * Calculate the sum of all numbers from 1 to n.
 */
console.log('\nExercise 2: Sum to N');

function sumToN(n) {
  // TODO: Implement recursively
}

// Test:
// console.log(sumToN(5));   // 15 (1+2+3+4+5)
// console.log(sumToN(10));  // 55

/**
 * Exercise 3: Factorial
 *
 * Calculate n! (n factorial).
 */
console.log('\nExercise 3: Factorial');

function factorial(n) {
  // TODO: Implement recursively
}

// Test:
// console.log(factorial(5));  // 120
// console.log(factorial(0));  // 1

/**
 * Exercise 4: Fibonacci
 *
 * Return the nth Fibonacci number.
 * Bonus: Make it efficient with memoization.
 */
console.log('\nExercise 4: Fibonacci');

function fibonacci(n) {
  // TODO: Implement recursively
}

// Test:
// console.log(fibonacci(10));  // 55
// console.log(fibonacci(20));  // 6765

/**
 * Exercise 5: Power
 *
 * Calculate base^exponent recursively.
 */
console.log('\nExercise 5: Power');

function power(base, exponent) {
  // TODO: Implement recursively
  // Bonus: Handle negative exponents
}

// Test:
// console.log(power(2, 10));  // 1024
// console.log(power(3, 4));   // 81

/**
 * Exercise 6: Sum Array
 *
 * Calculate the sum of all elements in an array.
 */
console.log('\nExercise 6: Sum Array');

function sumArray(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(sumArray([1, 2, 3, 4, 5]));  // 15
// console.log(sumArray([]));               // 0

/**
 * Exercise 7: Count Elements
 *
 * Count elements in an array (don't use .length in recursive calls).
 */
console.log('\nExercise 7: Count Elements');

function countElements(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(countElements([1, 2, 3, 4, 5]));  // 5
// console.log(countElements([]));               // 0

/**
 * Exercise 8: Reverse String
 *
 * Reverse a string recursively.
 */
console.log('\nExercise 8: Reverse String');

function reverseString(str) {
  // TODO: Implement recursively
}

// Test:
// console.log(reverseString("hello"));      // "olleh"
// console.log(reverseString("JavaScript")); // "tpircSavaJ"

/**
 * Exercise 9: Palindrome Check
 *
 * Check if a string is a palindrome.
 */
console.log('\nExercise 9: Palindrome Check');

function isPalindrome(str) {
  // TODO: Implement recursively
}

// Test:
// console.log(isPalindrome("racecar"));  // true
// console.log(isPalindrome("hello"));    // false
// console.log(isPalindrome("a"));        // true

/**
 * Exercise 10: Find Max
 *
 * Find the maximum value in an array.
 */
console.log('\nExercise 10: Find Max');

function findMax(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(findMax([3, 1, 4, 1, 5, 9, 2, 6]));  // 9
// console.log(findMax([1]));                       // 1

// =====================================================
// INTERMEDIATE EXERCISES
// =====================================================

/**
 * Exercise 11: Flatten Array
 *
 * Flatten a nested array to a single level.
 */
console.log('\nExercise 11: Flatten Array');

function flatten(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(flatten([1, [2, [3, [4]]]]));  // [1, 2, 3, 4]
// console.log(flatten([[1, 2], [3, [4, 5]]])); // [1, 2, 3, 4, 5]

/**
 * Exercise 12: Deep Clone
 *
 * Create a deep copy of an object.
 */
console.log('\nExercise 12: Deep Clone');

function deepClone(obj) {
  // TODO: Implement recursively
}

// Test:
// const original = { a: 1, b: { c: 2 } };
// const cloned = deepClone(original);
// console.log(cloned);  // { a: 1, b: { c: 2 } }
// console.log(cloned === original);  // false
// console.log(cloned.b === original.b);  // false

/**
 * Exercise 13: Binary Search
 *
 * Find index of target in sorted array, or -1 if not found.
 */
console.log('\nExercise 13: Binary Search');

function binarySearch(arr, target, left = 0, right = arr.length - 1) {
  // TODO: Implement recursively
}

// Test:
// const sorted = [1, 3, 5, 7, 9, 11, 13];
// console.log(binarySearch(sorted, 7));   // 3
// console.log(binarySearch(sorted, 8));   // -1

/**
 * Exercise 14: Count Nested Value
 *
 * Count occurrences of a value in nested arrays.
 */
console.log('\nExercise 14: Count Nested Value');

function countNested(arr, target) {
  // TODO: Implement recursively
}

// Test:
// console.log(countNested([1, [1, 2, [1, 3]], 1], 1));  // 4
// console.log(countNested([1, [2, [3]]], 5));           // 0

/**
 * Exercise 15: Get All Keys
 *
 * Get all keys from a nested object.
 */
console.log('\nExercise 15: Get All Keys');

function getAllKeys(obj) {
  // TODO: Implement recursively
}

// Test:
// const nested = { a: 1, b: { c: 2, d: { e: 3 } } };
// console.log(getAllKeys(nested));  // ["a", "b", "c", "d", "e"]

// =====================================================
// ADVANCED EXERCISES
// =====================================================

/**
 * Bonus 1: Generate Subsets
 *
 * Generate all subsets (power set) of an array.
 */
console.log('\nBonus 1: Generate Subsets');

function subsets(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(subsets([1, 2, 3]));
// [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

/**
 * Bonus 2: Merge Sort
 *
 * Implement merge sort recursively.
 */
console.log('\nBonus 2: Merge Sort');

function mergeSort(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(mergeSort([3, 1, 4, 1, 5, 9, 2, 6]));
// [1, 1, 2, 3, 4, 5, 6, 9]

/**
 * Bonus 3: Parse Nested JSON Path
 *
 * Get value from object using path string like "a.b.c".
 */
console.log('\nBonus 3: Parse Nested Path');

function getPath(obj, path) {
  // TODO: Implement recursively
}

// Test:
// const data = { a: { b: { c: 42 } } };
// console.log(getPath(data, "a.b.c"));  // 42
// console.log(getPath(data, "a.b"));    // { c: 42 }

/**
 * Bonus 4: GCD of Array
 *
 * Find greatest common divisor of all numbers in array.
 */
console.log('\nBonus 4: GCD of Array');

function gcdArray(arr) {
  // TODO: Implement recursively
}

// Test:
// console.log(gcdArray([12, 18, 24]));  // 6
// console.log(gcdArray([5, 10, 15]));   // 5

/**
 * Bonus 5: Tree Traversal
 *
 * Collect all values from a tree structure.
 */
console.log('\nBonus 5: Tree Traversal');

function collectValues(node) {
  // TODO: Implement recursively
}

// Test:
// const tree = {
//     value: 1,
//     children: [
//         { value: 2, children: [] },
//         { value: 3, children: [
//             { value: 4, children: [] }
//         ]}
//     ]
// };
// console.log(collectValues(tree));  // [1, 2, 3, 4]

// =====================================================
// SOLUTIONS (Uncomment to check your answers)
// =====================================================

/*
// Exercise 1 Solution:
function countdown(n) {
    if (n <= 0) {
        console.log("Blastoff!");
        return;
    }
    console.log(n);
    countdown(n - 1);
}

// Exercise 2 Solution:
function sumToN(n) {
    if (n <= 0) return 0;
    return n + sumToN(n - 1);
}

// Exercise 3 Solution:
function factorial(n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

// Exercise 4 Solution (with memoization):
function fibonacci(n, memo = {}) {
    if (n in memo) return memo[n];
    if (n <= 1) return n;
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
    return memo[n];
}

// Exercise 5 Solution:
function power(base, exponent) {
    if (exponent === 0) return 1;
    if (exponent < 0) return 1 / power(base, -exponent);
    return base * power(base, exponent - 1);
}

// Exercise 6 Solution:
function sumArray(arr, index = 0) {
    if (index >= arr.length) return 0;
    return arr[index] + sumArray(arr, index + 1);
}

// Exercise 7 Solution:
function countElements(arr) {
    if (arr.length === 0) return 0;
    return 1 + countElements(arr.slice(1));
}

// Exercise 8 Solution:
function reverseString(str) {
    if (str.length <= 1) return str;
    return reverseString(str.slice(1)) + str[0];
}

// Exercise 9 Solution:
function isPalindrome(str) {
    if (str.length <= 1) return true;
    if (str[0] !== str[str.length - 1]) return false;
    return isPalindrome(str.slice(1, -1));
}

// Exercise 10 Solution:
function findMax(arr, index = 0, max = -Infinity) {
    if (index >= arr.length) return max;
    return findMax(arr, index + 1, arr[index] > max ? arr[index] : max);
}

// Exercise 11 Solution:
function flatten(arr) {
    let result = [];
    for (const item of arr) {
        if (Array.isArray(item)) {
            result = result.concat(flatten(item));
        } else {
            result.push(item);
        }
    }
    return result;
}

// Exercise 12 Solution:
function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') return obj;
    if (Array.isArray(obj)) return obj.map(item => deepClone(item));
    
    const clone = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            clone[key] = deepClone(obj[key]);
        }
    }
    return clone;
}

// Exercise 13 Solution:
function binarySearch(arr, target, left = 0, right = arr.length - 1) {
    if (left > right) return -1;
    const mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) return mid;
    if (arr[mid] > target) return binarySearch(arr, target, left, mid - 1);
    return binarySearch(arr, target, mid + 1, right);
}

// Exercise 14 Solution:
function countNested(arr, target) {
    let count = 0;
    for (const item of arr) {
        if (Array.isArray(item)) {
            count += countNested(item, target);
        } else if (item === target) {
            count++;
        }
    }
    return count;
}

// Exercise 15 Solution:
function getAllKeys(obj) {
    let keys = [];
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            keys.push(key);
            if (typeof obj[key] === 'object' && obj[key] !== null) {
                keys = keys.concat(getAllKeys(obj[key]));
            }
        }
    }
    return keys;
}

// Bonus 1 Solution:
function subsets(arr) {
    if (arr.length === 0) return [[]];
    
    const first = arr[0];
    const rest = subsets(arr.slice(1));
    
    const withFirst = rest.map(subset => [first, ...subset]);
    return [...rest, ...withFirst];
}

// Bonus 2 Solution:
function mergeSort(arr) {
    if (arr.length <= 1) return arr;
    
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    
    return merge(left, right);
}

function merge(left, right) {
    const result = [];
    let i = 0, j = 0;
    
    while (i < left.length && j < right.length) {
        if (left[i] <= right[j]) {
            result.push(left[i++]);
        } else {
            result.push(right[j++]);
        }
    }
    
    return result.concat(left.slice(i)).concat(right.slice(j));
}

// Bonus 3 Solution:
function getPath(obj, path) {
    const parts = path.split('.');
    if (parts.length === 1) return obj[parts[0]];
    return getPath(obj[parts[0]], parts.slice(1).join('.'));
}

// Bonus 4 Solution:
function gcdArray(arr) {
    if (arr.length === 1) return arr[0];
    
    function gcd(a, b) {
        if (b === 0) return a;
        return gcd(b, a % b);
    }
    
    return gcd(arr[0], gcdArray(arr.slice(1)));
}

// Bonus 5 Solution:
function collectValues(node) {
    if (!node) return [];
    
    let values = [node.value];
    
    if (node.children) {
        for (const child of node.children) {
            values = values.concat(collectValues(child));
        }
    }
    
    return values;
}
*/
Exercises - JavaScript Tutorial | DeepML