javascript
exercises
exercises.js⚡javascript
/**
* ========================================
* 13.2 Regular Expressions - Exercises
* ========================================
*
* Practice working with JavaScript regex.
*/
/**
* EXERCISE 1: Basic Pattern Matching
*
* Write functions to test for:
* - Contains digits
* - Contains only letters
* - Contains only alphanumeric
*/
function patternChecks(str) {
// YOUR CODE HERE:
// Return { hasDigits, lettersOnly, alphanumericOnly }
}
// console.log(patternChecks('Hello123'));
// { hasDigits: true, lettersOnly: false, alphanumericOnly: true }
// console.log(patternChecks('Hello'));
// { hasDigits: false, lettersOnly: true, alphanumericOnly: true }
/*
* SOLUTION:
*
* function patternChecks(str) {
* return {
* hasDigits: /\d/.test(str),
* lettersOnly: /^[a-zA-Z]+$/.test(str),
* alphanumericOnly: /^[a-zA-Z0-9]+$/.test(str)
* };
* }
*/
/**
* EXERCISE 2: Email Validation
*
* Create a comprehensive email validator that checks:
* - Has @ symbol
* - Has valid domain
* - Has valid TLD (2+ chars)
*/
function validateEmail(email) {
// YOUR CODE HERE:
// Return true if valid email format
}
// console.log(validateEmail('user@example.com')); // true
// console.log(validateEmail('user.name@sub.domain.org')); // true
// console.log(validateEmail('invalid')); // false
// console.log(validateEmail('@example.com')); // false
/*
* SOLUTION:
*
* function validateEmail(email) {
* const pattern = /^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
* return pattern.test(email);
* }
*/
/**
* EXERCISE 3: Password Strength Checker
*
* Check password strength based on:
* - minLength (default 8)
* - requireUppercase
* - requireLowercase
* - requireDigit
* - requireSpecial (!@#$%^&*)
*/
function checkPasswordStrength(password, options = {}) {
// YOUR CODE HERE:
// Return { valid, issues: [] }
}
// console.log(checkPasswordStrength('Pass123!', {
// minLength: 8,
// requireUppercase: true,
// requireLowercase: true,
// requireDigit: true,
// requireSpecial: true
// }));
// { valid: true, issues: [] }
/*
* SOLUTION:
*
* function checkPasswordStrength(password, options = {}) {
* const {
* minLength = 8,
* requireUppercase = false,
* requireLowercase = false,
* requireDigit = false,
* requireSpecial = false
* } = options;
*
* const issues = [];
*
* if (password.length < minLength) {
* issues.push(`Must be at least ${minLength} characters`);
* }
* if (requireUppercase && !/[A-Z]/.test(password)) {
* issues.push('Must contain uppercase letter');
* }
* if (requireLowercase && !/[a-z]/.test(password)) {
* issues.push('Must contain lowercase letter');
* }
* if (requireDigit && !/\d/.test(password)) {
* issues.push('Must contain digit');
* }
* if (requireSpecial && !/[!@#$%^&*]/.test(password)) {
* issues.push('Must contain special character');
* }
*
* return { valid: issues.length === 0, issues };
* }
*/
/**
* EXERCISE 4: Extract All Matches
*
* Extract all occurrences with their positions.
*/
function findAllMatches(str, pattern) {
// YOUR CODE HERE:
// Return array of { match, index }
}
// console.log(findAllMatches('test1 test2 test3', /test\d/g));
// [
// { match: 'test1', index: 0 },
// { match: 'test2', index: 6 },
// { match: 'test3', index: 12 }
// ]
/*
* SOLUTION:
*
* function findAllMatches(str, pattern) {
* return [...str.matchAll(pattern)].map(match => ({
* match: match[0],
* index: match.index
* }));
* }
*/
/**
* EXERCISE 5: Parse Key-Value Pairs
*
* Parse a string of key=value pairs.
*/
function parseKeyValue(str) {
// YOUR CODE HERE:
// Return object with key-value pairs
}
// console.log(parseKeyValue('name=John; age=30; city=NYC'));
// { name: 'John', age: '30', city: 'NYC' }
/*
* SOLUTION:
*
* function parseKeyValue(str) {
* const pattern = /(\w+)=([^;]+)/g;
* const result = {};
*
* for (const match of str.matchAll(pattern)) {
* result[match[1]] = match[2].trim();
* }
*
* return result;
* }
*/
/**
* EXERCISE 6: Phone Number Formatter
*
* Detect and format various phone number formats.
*/
function formatPhoneNumber(phone) {
// YOUR CODE HERE:
// Accept various formats, output (XXX) XXX-XXXX
}
// console.log(formatPhoneNumber('5551234567')); // '(555) 123-4567'
// console.log(formatPhoneNumber('555-123-4567')); // '(555) 123-4567'
// console.log(formatPhoneNumber('(555) 123-4567')); // '(555) 123-4567'
// console.log(formatPhoneNumber('555.123.4567')); // '(555) 123-4567'
/*
* SOLUTION:
*
* function formatPhoneNumber(phone) {
* const digits = phone.replace(/\D/g, '');
*
* if (digits.length !== 10) {
* return null; // Invalid
* }
*
* return digits.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
* }
*/
/**
* EXERCISE 7: URL Parser
*
* Parse URL components using regex.
*/
function parseURL(url) {
// YOUR CODE HERE:
// Return { protocol, domain, port, path, query, hash }
}
// console.log(parseURL('https://example.com:8080/path/to/page?q=test#section'));
// {
// protocol: 'https',
// domain: 'example.com',
// port: '8080',
// path: '/path/to/page',
// query: 'q=test',
// hash: 'section'
// }
/*
* SOLUTION:
*
* function parseURL(url) {
* const pattern = /^(?<protocol>\w+):\/\/(?<domain>[\w.-]+)(?::(?<port>\d+))?(?<path>\/[^?#]*)?(?:\?(?<query>[^#]*))?(?:#(?<hash>.*))?$/;
* const match = url.match(pattern);
*
* if (!match) return null;
*
* return {
* protocol: match.groups.protocol || null,
* domain: match.groups.domain || null,
* port: match.groups.port || null,
* path: match.groups.path || '/',
* query: match.groups.query || null,
* hash: match.groups.hash || null
* };
* }
*/
/**
* EXERCISE 8: Credit Card Validation
*
* Validate and identify credit card type.
*/
function validateCreditCard(number) {
// YOUR CODE HERE:
// Return { valid, type } where type is Visa, Mastercard, Amex, or Unknown
}
// console.log(validateCreditCard('4111111111111111')); // { valid: true, type: 'Visa' }
// console.log(validateCreditCard('5500000000000004')); // { valid: true, type: 'Mastercard' }
// console.log(validateCreditCard('371449635398431')); // { valid: true, type: 'Amex' }
/*
* SOLUTION:
*
* function validateCreditCard(number) {
* const cleaned = number.replace(/\D/g, '');
*
* const patterns = {
* Visa: /^4\d{12}(?:\d{3})?$/,
* Mastercard: /^5[1-5]\d{14}$/,
* Amex: /^3[47]\d{13}$/
* };
*
* for (const [type, pattern] of Object.entries(patterns)) {
* if (pattern.test(cleaned)) {
* return { valid: true, type };
* }
* }
*
* return { valid: false, type: 'Unknown' };
* }
*/
/**
* EXERCISE 9: Highlight Search Terms
*
* Wrap all occurrences of search terms with HTML tags.
* Handle case-insensitivity and word boundaries.
*/
function highlightTerms(text, terms, tag = 'mark') {
// YOUR CODE HERE:
// Wrap each term occurrence with <tag></tag>
}
// console.log(highlightTerms('The quick brown fox', ['the', 'fox']));
// '<mark>The</mark> quick brown <mark>fox</mark>'
/*
* SOLUTION:
*
* function highlightTerms(text, terms, tag = 'mark') {
* const escaped = terms.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
* const pattern = new RegExp(`\\b(${escaped.join('|')})\\b`, 'gi');
* return text.replace(pattern, `<${tag}>$1</${tag}>`);
* }
*/
/**
* EXERCISE 10: Extract Markdown Links
*
* Extract all markdown links [text](url) from text.
*/
function extractMarkdownLinks(markdown) {
// YOUR CODE HERE:
// Return array of { text, url }
}
// const md = 'Check out [Google](https://google.com) and [GitHub](https://github.com)';
// console.log(extractMarkdownLinks(md));
// [
// { text: 'Google', url: 'https://google.com' },
// { text: 'GitHub', url: 'https://github.com' }
// ]
/*
* SOLUTION:
*
* function extractMarkdownLinks(markdown) {
* const pattern = /\[([^\]]+)\]\(([^)]+)\)/g;
* return [...markdown.matchAll(pattern)].map(match => ({
* text: match[1],
* url: match[2]
* }));
* }
*/
/**
* EXERCISE 11: Validate IP Address
*
* Validate IPv4 addresses (0-255 for each octet).
*/
function isValidIPv4(ip) {
// YOUR CODE HERE:
// Return true if valid IPv4
}
// console.log(isValidIPv4('192.168.1.1')); // true
// console.log(isValidIPv4('255.255.255.0')); // true
// console.log(isValidIPv4('256.1.1.1')); // false (256 > 255)
// console.log(isValidIPv4('1.1.1')); // false (only 3 octets)
/*
* SOLUTION:
*
* function isValidIPv4(ip) {
* const octet = '(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)';
* const pattern = new RegExp(`^${octet}\\.${octet}\\.${octet}\\.${octet}$`);
* return pattern.test(ip);
* }
*/
/**
* EXERCISE 12: Template Variable Replacement
*
* Replace {{variable}} placeholders with values from object.
*/
function templateReplace(template, data) {
// YOUR CODE HERE:
// Replace all {{key}} with data[key]
}
// const template = 'Hello {{name}}, you have {{count}} messages';
// console.log(templateReplace(template, { name: 'John', count: 5 }));
// 'Hello John, you have 5 messages'
/*
* SOLUTION:
*
* function templateReplace(template, data) {
* return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
* return data.hasOwnProperty(key) ? data[key] : match;
* });
* }
*/
/**
* EXERCISE 13: Tokenize Expression
*
* Tokenize a simple math expression.
*/
function tokenize(expression) {
// YOUR CODE HERE:
// Return array of tokens { type, value }
}
// console.log(tokenize('3 + 42 * 5 - 10'));
// [
// { type: 'number', value: '3' },
// { type: 'operator', value: '+' },
// { type: 'number', value: '42' },
// { type: 'operator', value: '*' },
// { type: 'number', value: '5' },
// { type: 'operator', value: '-' },
// { type: 'number', value: '10' }
// ]
/*
* SOLUTION:
*
* function tokenize(expression) {
* const pattern = /(\d+)|([+\-*/])/g;
* const tokens = [];
*
* for (const match of expression.matchAll(pattern)) {
* if (match[1]) {
* tokens.push({ type: 'number', value: match[1] });
* } else if (match[2]) {
* tokens.push({ type: 'operator', value: match[2] });
* }
* }
*
* return tokens;
* }
*/
/**
* EXERCISE 14: Find Repeated Words
*
* Find consecutive repeated words in text.
*/
function findRepeatedWords(text) {
// YOUR CODE HERE:
// Return array of repeated words
}
// console.log(findRepeatedWords('The the quick brown fox fox jumps'));
// ['the', 'fox']
/*
* SOLUTION:
*
* function findRepeatedWords(text) {
* const pattern = /\b(\w+)\s+\1\b/gi;
* return [...text.matchAll(pattern)].map(match => match[1].toLowerCase());
* }
*/
/**
* EXERCISE 15: Validate Date Format
*
* Validate dates in various formats and normalize.
*/
function validateAndNormalizeDate(dateStr) {
// YOUR CODE HERE:
// Accept: MM/DD/YYYY, MM-DD-YYYY, YYYY-MM-DD
// Return: { valid, normalized } where normalized is YYYY-MM-DD
}
// console.log(validateAndNormalizeDate('12/25/2024')); // { valid: true, normalized: '2024-12-25' }
// console.log(validateAndNormalizeDate('2024-12-25')); // { valid: true, normalized: '2024-12-25' }
// console.log(validateAndNormalizeDate('13/25/2024')); // { valid: false, normalized: null }
/*
* SOLUTION:
*
* function validateAndNormalizeDate(dateStr) {
* // MM/DD/YYYY or MM-DD-YYYY
* const mdyPattern = /^(0[1-9]|1[0-2])[\/\-](0[1-9]|[12]\d|3[01])[\/\-](\d{4})$/;
* // YYYY-MM-DD
* const ymdPattern = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
*
* let match = dateStr.match(mdyPattern);
* if (match) {
* return {
* valid: true,
* normalized: `${match[3]}-${match[1]}-${match[2]}`
* };
* }
*
* match = dateStr.match(ymdPattern);
* if (match) {
* return { valid: true, normalized: dateStr };
* }
*
* return { valid: false, normalized: null };
* }
*/
/**
* EXERCISE 16: Clean and Normalize Text
*
* Clean text by:
* - Removing extra whitespace
* - Removing special characters (keep alphanumeric and basic punctuation)
* - Normalizing quotes
*/
function cleanText(text) {
// YOUR CODE HERE:
// Return cleaned text
}
// console.log(cleanText(' Hello "World"!! How\'s it going??? '));
// 'Hello "World"! How\'s it going?'
/*
* SOLUTION:
*
* function cleanText(text) {
* return text
* .replace(/\s+/g, ' ') // Collapse whitespace
* .replace(/[""]/g, '"') // Normalize quotes
* .replace(/['']/g, "'") // Normalize apostrophes
* .replace(/([!?.])\1+/g, '$1') // Remove repeated punctuation
* .replace(/[^\w\s.,!?'"()-]/g, '') // Remove other special chars
* .trim();
* }
*/
/**
* EXERCISE 17: Extract Code Blocks
*
* Extract code blocks from markdown text.
*/
function extractCodeBlocks(markdown) {
// YOUR CODE HERE:
// Return array of { language, code }
}
// const md = `
// Here's some JavaScript:
// \`\`\`javascript
// const x = 1;
// console.log(x);
// \`\`\`
// And some Python:
// \`\`\`python
// x = 1
// print(x)
// \`\`\`
// `;
// console.log(extractCodeBlocks(md));
// [
// { language: 'javascript', code: 'const x = 1;\nconsole.log(x);' },
// { language: 'python', code: 'x = 1\nprint(x)' }
// ]
/*
* SOLUTION:
*
* function extractCodeBlocks(markdown) {
* const pattern = /```(\w*)\n([\s\S]*?)```/g;
* return [...markdown.matchAll(pattern)].map(match => ({
* language: match[1] || 'text',
* code: match[2].trim()
* }));
* }
*/
/**
* EXERCISE 18: Mask Sensitive Data
*
* Mask various types of sensitive data.
*/
function maskSensitiveData(text) {
// YOUR CODE HERE:
// Mask emails, phone numbers, credit cards
}
// const text = 'Email: john@example.com, Phone: 555-123-4567, Card: 4111111111111111';
// console.log(maskSensitiveData(text));
// 'Email: j***@***.com, Phone: ***-***-4567, Card: ************1111'
/*
* SOLUTION:
*
* function maskSensitiveData(text) {
* return text
* // Mask email (show first char and domain suffix)
* .replace(/([\w.-]+)@([\w.-]+)\.(\w+)/g, (m, user, domain, tld) =>
* `${user[0]}***@***.${tld}`
* )
* // Mask phone (show last 4 digits)
* .replace(/(\d{3})[-.]?(\d{3})[-.]?(\d{4})/g, '***-***-$3')
* // Mask credit card (show last 4 digits)
* .replace(/\d{12}(\d{4})/g, '************$1');
* }
*/
/**
* EXERCISE 19: Slugify with Transliteration
*
* Create URL-friendly slugs, handling accented characters.
*/
function slugify(text) {
// YOUR CODE HERE:
// Convert to lowercase, replace accents, remove special chars, replace spaces with hyphens
}
// console.log(slugify('Hello World!')); // 'hello-world'
// console.log(slugify('Café & Restaurant')); // 'cafe-restaurant'
// console.log(slugify(' Multiple Spaces ')); // 'multiple-spaces'
/*
* SOLUTION:
*
* function slugify(text) {
* const accents = {
* 'à|á|â|ã|ä|å': 'a',
* 'è|é|ê|ë': 'e',
* 'ì|í|î|ï': 'i',
* 'ò|ó|ô|õ|ö': 'o',
* 'ù|ú|û|ü': 'u',
* 'ñ': 'n',
* 'ç': 'c'
* };
*
* let slug = text.toLowerCase();
*
* for (const [pattern, replacement] of Object.entries(accents)) {
* slug = slug.replace(new RegExp(pattern, 'g'), replacement);
* }
*
* return slug
* .replace(/[^\w\s-]/g, '') // Remove special chars
* .replace(/\s+/g, '-') // Replace spaces with hyphens
* .replace(/-+/g, '-') // Remove multiple hyphens
* .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
* }
*/
/**
* EXERCISE 20: Build Dynamic Regex
*
* Create a function that builds a regex from user input safely.
*/
function buildSearchRegex(searchTerm, options = {}) {
// YOUR CODE HERE:
// Build regex with options: wholeWord, caseSensitive, startsWith, endsWith
}
// const regex = buildSearchRegex('test', { wholeWord: true, caseSensitive: false });
// console.log(regex.test('This is a TEST')); // true
// console.log(regex.test('testing')); // false (not whole word)
/*
* SOLUTION:
*
* function buildSearchRegex(searchTerm, options = {}) {
* const {
* wholeWord = false,
* caseSensitive = true,
* startsWith = false,
* endsWith = false
* } = options;
*
* // Escape special regex characters
* let pattern = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
*
* if (wholeWord) {
* pattern = `\\b${pattern}\\b`;
* }
* if (startsWith) {
* pattern = `^${pattern}`;
* }
* if (endsWith) {
* pattern = `${pattern}$`;
* }
*
* const flags = caseSensitive ? '' : 'i';
* return new RegExp(pattern, flags);
* }
*/
console.log('Regular expressions exercises loaded!');