javascript

exercises

exercises.js
/**
 * ============================================
 * 3.9 NULLISH COALESCING - EXERCISES
 * ============================================
 */

// ============================================
// EXERCISE 1: Basic Nullish Coalescing
// ============================================
/**
 * Task: Use ?? to provide defaults for these values
 * - For null value, default to "Not available"
 * - For undefined value, default to "Unknown"
 * - For 0 value, keep the 0
 * - For empty string, keep the empty string
 */

const nullValue = null;
const undefinedValue = undefined;
const zeroValue = 0;
const emptyString = '';

// Your code here:
// const result1 = nullValue ?? ...
// const result2 = undefinedValue ?? ...
// const result3 = zeroValue ?? ...
// const result4 = emptyString ?? ...

// console.log("Exercise 1:");
// console.log("null:", result1);       // "Not available"
// console.log("undefined:", result2);  // "Unknown"
// console.log("0:", result3);          // 0
// console.log("'':", result4);         // ""

// ============================================
// EXERCISE 2: ?? vs || Comparison
// ============================================
/**
 * Task: For the value 0, show the difference between ?? and ||
 */

const count = 0;
// Your code here:
// const withOr = count || ...
// const withNullish = count ?? ...

// console.log("Exercise 2:");
// console.log("Using ||:", withOr);      // "No items"
// console.log("Using ??:", withNullish); // 0

// ============================================
// EXERCISE 3: Configuration Object
// ============================================
/**
 * Task: Create a function that merges user config with defaults
 * using ?? to preserve 0, false, and empty string values
 *
 * Defaults: { theme: 'light', volume: 50, muted: false }
 */

// Your code here:
// function mergeConfig(userConfig) { }

const userConfig = { theme: 'dark', volume: 0, muted: true };

// console.log("Exercise 3:");
// console.log(mergeConfig(userConfig));
// // { theme: 'dark', volume: 0, muted: true }
// console.log(mergeConfig({}));
// // { theme: 'light', volume: 50, muted: false }

// ============================================
// EXERCISE 4: Chained Nullish Coalescing
// ============================================
/**
 * Task: Given these variables, use chained ?? to find
 * the first non-nullish value
 */

const primary = null;
const secondary = undefined;
const tertiary = 0;
const fallback = 'default';

// Your code here:
// const result = ...

// console.log("Exercise 4:", result);  // 0

// ============================================
// EXERCISE 5: Nullish Assignment
// ============================================
/**
 * Task: Use ??= to set default values only where needed
 */

const settings = {
  color: null,
  size: 0,
  enabled: false,
  label: undefined,
};

// Your code here - use ??= to set defaults:
// settings.color ??= ...
// settings.size ??= ...
// settings.enabled ??= ...
// settings.label ??= ...

// console.log("Exercise 5:", settings);
// // { color: 'red', size: 0, enabled: false, label: 'Default' }

// ============================================
// EXERCISE 6: Form Input Processing
// ============================================
/**
 * Task: Process form input where empty strings and 0 are valid
 * Only null/undefined should get defaults
 */

function processInput(input) {
  // Your code here:
  // return {
  //     name: ...,
  //     age: ...,
  //     bio: ...,
  //     notifications: ...
  // };
}

// console.log("Exercise 6:");
// console.log(processInput({ name: '', age: 0, bio: null, notifications: false }));
// // { name: '', age: 0, bio: 'No bio', notifications: false }

// ============================================
// EXERCISE 7: With Optional Chaining
// ============================================
/**
 * Task: Safely access nested properties and provide defaults
 */

const user = {
  profile: {
    settings: {
      theme: 'dark',
    },
  },
};

// Your code here - use ?. and ??:
// const theme = ...
// const fontSize = ...
// const volume = ...

// console.log("Exercise 7:");
// console.log("theme:", theme);      // 'dark'
// console.log("fontSize:", fontSize); // 14 (default)
// console.log("volume:", volume);     // 50 (default)

// ============================================
// EXERCISE 8: API Response Handler
// ============================================
/**
 * Task: Handle API response data with proper defaults
 * Some fields might be null, undefined, 0, or empty string
 */

function handleResponse(response) {
  // Your code here:
  // const userName = ...
  // const score = ...
  // const message = ...
  // const isActive = ...
  // return { userName, score, message, isActive };
}

const apiResponse = {
  user: { name: 'John', score: 0 },
  message: '',
  isActive: false,
};

// console.log("Exercise 8:");
// console.log(handleResponse(apiResponse));
// // { userName: 'John', score: 0, message: '', isActive: false }

// console.log(handleResponse({}));
// // { userName: 'Guest', score: 0, message: 'Welcome!', isActive: true }

// ============================================
// EXERCISE 9: Fix the Bug
// ============================================
/**
 * Task: This code has a bug - it uses || when it should use ??
 * Fix it so that 0 and false are preserved
 */

function getDisplayValue(value, defaultValue) {
  // Bug: Using || instead of ??
  return value || defaultValue; // Fix this line
}

// console.log("Exercise 9:");
// console.log(getDisplayValue(0, 100));      // Should be 0, not 100
// console.log(getDisplayValue(false, true)); // Should be false, not true
// console.log(getDisplayValue(null, 'N/A')); // Should be 'N/A'

// ============================================
// EXERCISE 10: Lazy Initialization
// ============================================
/**
 * Task: Create a cache that lazily initializes values using ??=
 */

const cache = {};

function getOrCompute(key, computeFn) {
  // Your code here - use ??=
  // cache[key] ??= ...
  // return cache[key];
}

// const expensiveCompute = () => {
//     console.log("Computing...");
//     return Math.random();
// };

// console.log("Exercise 10:");
// console.log("First call:", getOrCompute('random', expensiveCompute));
// console.log("Second call:", getOrCompute('random', expensiveCompute));
// // Should only log "Computing..." once

// ============================================
// EXERCISE 11: Parentheses Required
// ============================================
/**
 * Task: Correctly combine ?? with || using parentheses
 *
 * Get the first truthy value from a and b,
 * or use 'fallback' if both are nullish
 */

const a = 0; // falsy but not nullish
const b = false; // falsy but not nullish
const c = null; // nullish

// Your code here - needs parentheses:
// const result = (a || b) ?? 'fallback';

// console.log("Exercise 11:", result);  // false (b is truthy compared to 0)

// ============================================
// EXERCISE 12: Complete Function
// ============================================
/**
 * Task: Create a function that handles all edge cases
 * for rendering a user profile
 */

function renderProfile(user) {
  // Your code here - handle all cases:
  // - name could be null/undefined/empty string
  // - age could be null/undefined/0
  // - bio could be null/undefined/empty string
  // - isVerified could be null/undefined/false
  // return {
  //     displayName: ...,
  //     displayAge: ...,
  //     displayBio: ...,
  //     verifiedBadge: ...
  // };
}

// console.log("Exercise 12:");
// console.log(renderProfile({ name: '', age: 0, bio: '', isVerified: false }));
// // { displayName: '', displayAge: 0, displayBio: '', verifiedBadge: false }

// console.log(renderProfile({}));
// // { displayName: 'Anonymous', displayAge: 'N/A', displayBio: 'No bio', verifiedBadge: false }

// ============================================
// SOLUTIONS
// ============================================
/**
 * Exercise 1:
 * const result1 = nullValue ?? "Not available";
 * const result2 = undefinedValue ?? "Unknown";
 * const result3 = zeroValue ?? "Default";
 * const result4 = emptyString ?? "Empty";
 *
 * Exercise 2:
 * const withOr = count || "No items";
 * const withNullish = count ?? "No items";
 *
 * Exercise 3:
 * function mergeConfig(userConfig) {
 *     return {
 *         theme: userConfig.theme ?? 'light',
 *         volume: userConfig.volume ?? 50,
 *         muted: userConfig.muted ?? false
 *     };
 * }
 *
 * Exercise 4:
 * const result = primary ?? secondary ?? tertiary ?? fallback;
 *
 * Exercise 5:
 * settings.color ??= 'red';
 * settings.size ??= 100;
 * settings.enabled ??= true;
 * settings.label ??= 'Default';
 *
 * Exercise 6:
 * function processInput(input) {
 *     return {
 *         name: input.name ?? 'Guest',
 *         age: input.age ?? 0,
 *         bio: input.bio ?? 'No bio',
 *         notifications: input.notifications ?? true
 *     };
 * }
 *
 * Exercise 7:
 * const theme = user?.profile?.settings?.theme ?? 'light';
 * const fontSize = user?.profile?.settings?.fontSize ?? 14;
 * const volume = user?.profile?.settings?.volume ?? 50;
 *
 * Exercise 8:
 * function handleResponse(response) {
 *     const userName = response?.user?.name ?? 'Guest';
 *     const score = response?.user?.score ?? 0;
 *     const message = response?.message ?? 'Welcome!';
 *     const isActive = response?.isActive ?? true;
 *     return { userName, score, message, isActive };
 * }
 *
 * Exercise 9:
 * function getDisplayValue(value, defaultValue) {
 *     return value ?? defaultValue;
 * }
 *
 * Exercise 10:
 * function getOrCompute(key, computeFn) {
 *     cache[key] ??= computeFn();
 *     return cache[key];
 * }
 *
 * Exercise 11:
 * const result = (a || b) ?? 'fallback';  // false
 *
 * Exercise 12:
 * function renderProfile(user) {
 *     return {
 *         displayName: user.name ?? 'Anonymous',
 *         displayAge: user.age ?? 'N/A',
 *         displayBio: user.bio ?? 'No bio',
 *         verifiedBadge: user.isVerified ?? false
 *     };
 * }
 */
Exercises - JavaScript Tutorial | DeepML