javascript

exercises

exercises.js
/**
 * ========================================
 * 13.1 String Fundamentals - Exercises
 * ========================================
 *
 * Practice working with JavaScript strings.
 */

/**
 * EXERCISE 1: Character Access
 *
 * Write a function that returns the first, middle, and last
 * characters of a string. Use .at() for negative indexing.
 */

function getCharacters(str) {
  // YOUR CODE HERE:
  // Return { first, middle, last }
}

// console.log(getCharacters('hello'));   // { first: 'h', middle: 'l', last: 'o' }
// console.log(getCharacters('JavaScript')); // { first: 'J', middle: 'S', last: 't' }

/*
 * SOLUTION:
 *
 * function getCharacters(str) {
 *     return {
 *         first: str.at(0),
 *         middle: str.at(Math.floor(str.length / 2)),
 *         last: str.at(-1)
 *     };
 * }
 */

/**
 * EXERCISE 2: Count Vowels and Consonants
 *
 * Count the number of vowels and consonants in a string.
 * Only count letters (ignore numbers and special characters).
 */

function countLetters(str) {
  // YOUR CODE HERE:
  // Return { vowels, consonants }
}

// console.log(countLetters('Hello World'));  // { vowels: 3, consonants: 7 }
// console.log(countLetters('JavaScript123')); // { vowels: 3, consonants: 7 }

/*
 * SOLUTION:
 *
 * function countLetters(str) {
 *     const vowels = 'aeiouAEIOU';
 *     let vowelCount = 0;
 *     let consonantCount = 0;
 *
 *     for (const char of str) {
 *         if (/[a-zA-Z]/.test(char)) {
 *             if (vowels.includes(char)) {
 *                 vowelCount++;
 *             } else {
 *                 consonantCount++;
 *             }
 *         }
 *     }
 *
 *     return { vowels: vowelCount, consonants: consonantCount };
 * }
 */

/**
 * EXERCISE 3: Template Literal Builder
 *
 * Create a function that builds a formatted message
 * using template literals.
 */

function buildMessage(user) {
  // YOUR CODE HERE:
  // Return formatted string like:
  // "Hello, John Doe! You are 30 years old and live in NYC."
}

// const user = { firstName: 'John', lastName: 'Doe', age: 30, city: 'NYC' };
// console.log(buildMessage(user));

/*
 * SOLUTION:
 *
 * function buildMessage(user) {
 *     return `Hello, ${user.firstName} ${user.lastName}! You are ${user.age} years old and live in ${user.city}.`;
 * }
 */

/**
 * EXERCISE 4: Case Converter
 *
 * Create functions for different case conversions:
 * - toTitleCase: "hello world" → "Hello World"
 * - toCamelCase: "hello world" → "helloWorld"
 * - toSnakeCase: "Hello World" → "hello_world"
 * - toKebabCase: "Hello World" → "hello-world"
 */

function caseConverter(str) {
  // YOUR CODE HERE:
  // Return { titleCase, camelCase, snakeCase, kebabCase }
}

// console.log(caseConverter('hello world'));
// {
//     titleCase: 'Hello World',
//     camelCase: 'helloWorld',
//     snakeCase: 'hello_world',
//     kebabCase: 'hello-world'
// }

/*
 * SOLUTION:
 *
 * function caseConverter(str) {
 *     const words = str.toLowerCase().split(/\s+/);
 *
 *     return {
 *         titleCase: words.map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
 *         camelCase: words.map((w, i) =>
 *             i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)
 *         ).join(''),
 *         snakeCase: words.join('_'),
 *         kebabCase: words.join('-')
 *     };
 * }
 */

/**
 * EXERCISE 5: Find All Occurrences
 *
 * Find all indices where a substring appears.
 */

function findAllOccurrences(str, search) {
  // YOUR CODE HERE:
  // Return array of indices
}

// console.log(findAllOccurrences('abcabc', 'abc'));  // [0, 3]
// console.log(findAllOccurrences('aaaa', 'aa'));     // [0, 1, 2]
// console.log(findAllOccurrences('hello', 'x'));    // []

/*
 * SOLUTION:
 *
 * function findAllOccurrences(str, search) {
 *     const indices = [];
 *     let index = 0;
 *
 *     while ((index = str.indexOf(search, index)) !== -1) {
 *         indices.push(index);
 *         index++;  // Move forward to find overlapping matches
 *     }
 *
 *     return indices;
 * }
 */

/**
 * EXERCISE 6: Word Counter
 *
 * Count words, sentences, and paragraphs in text.
 */

function analyzeText(text) {
  // YOUR CODE HERE:
  // Return { words, sentences, paragraphs }
}

// const text = `Hello World. This is a test.
//
// This is paragraph two.`;
// console.log(analyzeText(text));
// { words: 9, sentences: 3, paragraphs: 2 }

/*
 * SOLUTION:
 *
 * function analyzeText(text) {
 *     const words = text.trim().split(/\s+/).filter(w => w.length > 0).length;
 *     const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0).length;
 *     const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim().length > 0).length;
 *
 *     return { words, sentences, paragraphs };
 * }
 */

/**
 * EXERCISE 7: Truncate with Options
 *
 * Truncate a string with configurable options:
 * - maxLength: maximum length including suffix
 * - suffix: what to append (default '...')
 * - wordBoundary: if true, don't cut words
 */

function truncate(str, options = {}) {
  // YOUR CODE HERE:
  // Return truncated string
}

// console.log(truncate('Hello World', { maxLength: 8 }));  // 'Hello...'
// console.log(truncate('Hello World', { maxLength: 8, suffix: '…' }));  // 'Hello W…'
// console.log(truncate('Hello World', { maxLength: 8, wordBoundary: true }));  // 'Hello...'

/*
 * SOLUTION:
 *
 * function truncate(str, options = {}) {
 *     const { maxLength = 20, suffix = '...', wordBoundary = false } = options;
 *
 *     if (str.length <= maxLength) return str;
 *
 *     let truncated = str.slice(0, maxLength - suffix.length);
 *
 *     if (wordBoundary) {
 *         const lastSpace = truncated.lastIndexOf(' ');
 *         if (lastSpace > 0) {
 *             truncated = truncated.slice(0, lastSpace);
 *         }
 *     }
 *
 *     return truncated + suffix;
 * }
 */

/**
 * EXERCISE 8: String Padding Formatter
 *
 * Format data into aligned columns.
 */

function formatTable(data, columnWidth) {
  // YOUR CODE HERE:
  // Return formatted string with aligned columns
}

// const data = [
//     { name: 'Apple', price: 1.5 },
//     { name: 'Banana', price: 0.75 },
//     { name: 'Cherry', price: 2.0 }
// ];
// console.log(formatTable(data, 10));
// 'Apple     $    1.50'
// 'Banana    $    0.75'
// 'Cherry    $    2.00'

/*
 * SOLUTION:
 *
 * function formatTable(data, columnWidth) {
 *     return data.map(item =>
 *         item.name.padEnd(columnWidth) +
 *         '$' + item.price.toFixed(2).padStart(8)
 *     ).join('\n');
 * }
 */

/**
 * EXERCISE 9: Parse URL
 *
 * Extract parts of a URL string without using URL API.
 */

function parseUrl(url) {
  // YOUR CODE HERE:
  // Return { protocol, domain, path, query }
}

// console.log(parseUrl('https://example.com/path/to/page?name=John&age=30'));
// {
//     protocol: 'https',
//     domain: 'example.com',
//     path: '/path/to/page',
//     query: { name: 'John', age: '30' }
// }

/*
 * SOLUTION:
 *
 * function parseUrl(url) {
 *     const [protocolPart, rest] = url.split('://');
 *     const [domainAndPath, queryString] = rest.split('?');
 *     const [domain, ...pathParts] = domainAndPath.split('/');
 *     const path = '/' + pathParts.join('/');
 *
 *     const query = {};
 *     if (queryString) {
 *         queryString.split('&').forEach(pair => {
 *             const [key, value] = pair.split('=');
 *             query[key] = value;
 *         });
 *     }
 *
 *     return { protocol: protocolPart, domain, path, query };
 * }
 */

/**
 * EXERCISE 10: Replace Multiple
 *
 * Replace multiple substrings at once using a mapping object.
 */

function replaceMultiple(str, replacements) {
  // YOUR CODE HERE:
  // Replace all occurrences of each key with its value
}

// const text = 'Hello {name}, welcome to {city}!';
// console.log(replaceMultiple(text, { '{name}': 'John', '{city}': 'NYC' }));
// 'Hello John, welcome to NYC!'

/*
 * SOLUTION:
 *
 * function replaceMultiple(str, replacements) {
 *     let result = str;
 *     for (const [search, replace] of Object.entries(replacements)) {
 *         result = result.replaceAll(search, replace);
 *     }
 *     return result;
 * }
 *
 * // Alternative with regex:
 * function replaceMultiple(str, replacements) {
 *     const pattern = new RegExp(
 *         Object.keys(replacements).map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'),
 *         'g'
 *     );
 *     return str.replace(pattern, match => replacements[match]);
 * }
 */

/**
 * EXERCISE 11: Extract Hashtags
 *
 * Extract all hashtags from a social media post.
 */

function extractHashtags(text) {
  // YOUR CODE HERE:
  // Return array of hashtags (without #)
}

// console.log(extractHashtags('Hello #world! Check out #JavaScript and #coding'));
// ['world', 'JavaScript', 'coding']

/*
 * SOLUTION:
 *
 * function extractHashtags(text) {
 *     const matches = text.match(/#\w+/g) || [];
 *     return matches.map(tag => tag.slice(1));
 * }
 */

/**
 * EXERCISE 12: Mask String
 *
 * Mask part of a string (like credit card or email).
 */

function maskString(str, options = {}) {
  // YOUR CODE HERE:
  // Options: { showFirst, showLast, maskChar }
}

// console.log(maskString('1234567890123456', { showFirst: 4, showLast: 4 }));
// '1234********3456'
// console.log(maskString('john@example.com', { showFirst: 2, showLast: 4 }));
// 'jo**********com'

/*
 * SOLUTION:
 *
 * function maskString(str, options = {}) {
 *     const { showFirst = 0, showLast = 0, maskChar = '*' } = options;
 *
 *     if (showFirst + showLast >= str.length) return str;
 *
 *     const first = str.slice(0, showFirst);
 *     const last = str.slice(-showLast || str.length);
 *     const maskLength = str.length - showFirst - showLast;
 *
 *     return first + maskChar.repeat(maskLength) + last;
 * }
 */

/**
 * EXERCISE 13: Word Wrap
 *
 * Wrap text to a specified line length.
 */

function wordWrap(text, maxLineLength) {
  // YOUR CODE HERE:
  // Return text with newlines inserted at word boundaries
}

// const longText = 'The quick brown fox jumps over the lazy dog.';
// console.log(wordWrap(longText, 15));
// 'The quick brown
//  fox jumps over
//  the lazy dog.'

/*
 * SOLUTION:
 *
 * function wordWrap(text, maxLineLength) {
 *     const words = text.split(' ');
 *     const lines = [];
 *     let currentLine = '';
 *
 *     for (const word of words) {
 *         if (currentLine.length + word.length + 1 <= maxLineLength) {
 *             currentLine += (currentLine ? ' ' : '') + word;
 *         } else {
 *             if (currentLine) lines.push(currentLine);
 *             currentLine = word;
 *         }
 *     }
 *     if (currentLine) lines.push(currentLine);
 *
 *     return lines.join('\n');
 * }
 */

/**
 * EXERCISE 14: Slug Generator
 *
 * Convert a title to a URL-friendly slug.
 */

function generateSlug(title) {
  // YOUR CODE HERE:
  // Return lowercase slug with hyphens
}

// console.log(generateSlug('Hello World!'));  // 'hello-world'
// console.log(generateSlug('  JavaScript   Fundamentals  '));  // 'javascript-fundamentals'
// console.log(generateSlug("It's a Test!!"));  // 'its-a-test'

/*
 * SOLUTION:
 *
 * function generateSlug(title) {
 *     return title
 *         .toLowerCase()
 *         .trim()
 *         .replace(/[^\w\s-]/g, '')  // Remove special chars
 *         .replace(/\s+/g, '-')       // Replace spaces with hyphens
 *         .replace(/-+/g, '-');       // Remove multiple hyphens
 * }
 */

/**
 * EXERCISE 15: Anagram Checker
 *
 * Check if two strings are anagrams.
 */

function areAnagrams(str1, str2) {
  // YOUR CODE HERE:
  // Return true if strings are anagrams (ignore case and spaces)
}

// console.log(areAnagrams('listen', 'silent'));  // true
// console.log(areAnagrams('Hello', 'World'));    // false
// console.log(areAnagrams('Astronomer', 'Moon starer'));  // true

/*
 * SOLUTION:
 *
 * function areAnagrams(str1, str2) {
 *     const normalize = str => str
 *         .toLowerCase()
 *         .replace(/[^a-z]/g, '')
 *         .split('')
 *         .sort()
 *         .join('');
 *
 *     return normalize(str1) === normalize(str2);
 * }
 */

/**
 * EXERCISE 16: Compress String (Run-Length Encoding)
 *
 * Compress consecutive repeated characters.
 */

function compressString(str) {
  // YOUR CODE HERE:
  // Return compressed string
}

// console.log(compressString('aabbbcccc'));  // 'a2b3c4'
// console.log(compressString('abcd'));       // 'abcd' (no compression needed)
// console.log(compressString('aaAAbbBB'));   // 'a2A2b2B2'

/*
 * SOLUTION:
 *
 * function compressString(str) {
 *     let result = '';
 *     let count = 1;
 *
 *     for (let i = 0; i < str.length; i++) {
 *         if (str[i] === str[i + 1]) {
 *             count++;
 *         } else {
 *             result += str[i] + (count > 1 ? count : '');
 *             count = 1;
 *         }
 *     }
 *
 *     return result;
 * }
 */

/**
 * EXERCISE 17: Longest Common Prefix
 *
 * Find the longest common prefix of an array of strings.
 */

function longestCommonPrefix(strings) {
  // YOUR CODE HERE:
  // Return the common prefix
}

// console.log(longestCommonPrefix(['flower', 'flow', 'flight']));  // 'fl'
// console.log(longestCommonPrefix(['dog', 'racecar', 'car']));     // ''
// console.log(longestCommonPrefix(['test', 'testing', 'tester'])); // 'test'

/*
 * SOLUTION:
 *
 * function longestCommonPrefix(strings) {
 *     if (strings.length === 0) return '';
 *
 *     let prefix = strings[0];
 *
 *     for (let i = 1; i < strings.length; i++) {
 *         while (!strings[i].startsWith(prefix)) {
 *             prefix = prefix.slice(0, -1);
 *             if (prefix === '') return '';
 *         }
 *     }
 *
 *     return prefix;
 * }
 */

/**
 * EXERCISE 18: Highlight Search Term
 *
 * Wrap occurrences of a search term with HTML tags.
 */

function highlightSearch(text, term, tag = 'mark') {
  // YOUR CODE HERE:
  // Return text with term wrapped in tags (case-insensitive)
}

// console.log(highlightSearch('Hello World', 'world'));
// 'Hello <mark>World</mark>'
// console.log(highlightSearch('The quick brown fox', 'THE', 'strong'));
// '<strong>The</strong> quick brown fox'

/*
 * SOLUTION:
 *
 * function highlightSearch(text, term, tag = 'mark') {
 *     const regex = new RegExp(`(${term})`, 'gi');
 *     return text.replace(regex, `<${tag}>$1</${tag}>`);
 * }
 */

/**
 * EXERCISE 19: String Interleave
 *
 * Interleave two strings character by character.
 */

function interleave(str1, str2) {
  // YOUR CODE HERE:
  // Return interleaved string
}

// console.log(interleave('abc', '123'));   // 'a1b2c3'
// console.log(interleave('ab', '1234'));   // 'a1b234'
// console.log(interleave('abcd', '12'));   // 'a1b2cd'

/*
 * SOLUTION:
 *
 * function interleave(str1, str2) {
 *     const maxLen = Math.max(str1.length, str2.length);
 *     let result = '';
 *
 *     for (let i = 0; i < maxLen; i++) {
 *         if (i < str1.length) result += str1[i];
 *         if (i < str2.length) result += str2[i];
 *     }
 *
 *     return result;
 * }
 */

/**
 * EXERCISE 20: String Difference
 *
 * Find what was added and removed between two versions.
 */

function stringDiff(original, modified) {
  // YOUR CODE HERE:
  // Return { added, removed } (as arrays of words)
}

// console.log(stringDiff('hello world', 'hello there'));
// { added: ['there'], removed: ['world'] }

/*
 * SOLUTION:
 *
 * function stringDiff(original, modified) {
 *     const originalWords = new Set(original.split(/\s+/));
 *     const modifiedWords = new Set(modified.split(/\s+/));
 *
 *     const added = [...modifiedWords].filter(w => !originalWords.has(w));
 *     const removed = [...originalWords].filter(w => !modifiedWords.has(w));
 *
 *     return { added, removed };
 * }
 */

console.log('String fundamentals exercises loaded!');
Exercises - JavaScript Tutorial | DeepML