Cookbook - Practical JavaScript Recipes
JavaScript Cookbook 👨🍳
Section titled “JavaScript Cookbook 👨🍳”Welcome to the JavaScript Cookbook! This collection contains practical, ready-to-use code snippets and solutions for common programming challenges.
Array Manipulations
Section titled “Array Manipulations”Array Grouping
Section titled “Array Grouping”Group array elements by a specific property.
// Group objects by propertyfunction groupBy(array, key) { return array.reduce((groups, item) => { const group = item[key]; groups[group] = groups[group] || []; groups[group].push(item); return groups; }, {});}
const users = [ { name: 'Alice', department: 'Engineering' }, { name: 'Bob', department: 'Marketing' }, { name: 'Charlie', department: 'Engineering' }];
const grouped = groupBy(users, 'department');// { Engineering: [Alice, Charlie], Marketing: [Bob] }
// Modern approach with Object.groupBy() (ES2024)const modernGrouped = Object.groupBy(users, user => user.department);
Array Deduplication
Section titled “Array Deduplication”Remove duplicate values from arrays.
// Simple deduplicationconst numbers = [1, 2, 2, 3, 3, 4, 5];const unique = [...new Set(numbers)]; // [1, 2, 3, 4, 5]
// Deduplicate objects by propertyfunction uniqueBy(array, key) { const seen = new Set(); return array.filter(item => { const value = item[key]; if (seen.has(value)) return false; seen.add(value); return true; });}
const products = [ { id: 1, name: 'Phone' }, { id: 2, name: 'Laptop' }, { id: 1, name: 'Phone' }];
const uniqueProducts = uniqueBy(products, 'id');// [{ id: 1, name: 'Phone' }, { id: 2, name: 'Laptop' }]
Array Chunking
Section titled “Array Chunking”Split arrays into smaller chunks.
function chunk(array, size) { const chunks = []; for (let i = 0; i < array.length; i += size) { chunks.push(array.slice(i, i + size)); } return chunks;}
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9];const chunked = chunk(items, 3);// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// Functional approachconst functionalChunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) }, (_, i) => arr.slice(i * size, i * size + size) );
String Utilities
Section titled “String Utilities”String Formatting
Section titled “String Formatting”Various string manipulation utilities.
// Title casefunction titleCase(str) { return str .toLowerCase() .split(' ') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' ');}
// Camel casefunction camelCase(str) { return str .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => index === 0 ? word.toLowerCase() : word.toUpperCase() ) .replace(/\s+/g, '');}
// Snake casefunction snakeCase(str) { return str .replace(/\W+/g, ' ') .split(/ |\B(?=[A-Z])/) .map(word => word.toLowerCase()) .join('_');}
// Kebab casefunction kebabCase(str) { return str .replace(/([a-z])([A-Z])/g, '$1-$2') .replace(/\s+/g, '-') .toLowerCase();}
console.log(titleCase('hello world')); // "Hello World"console.log(camelCase('hello world')); // "helloWorld"console.log(snakeCase('Hello World')); // "hello_world"console.log(kebabCase('Hello World')); // "hello-world"
String Truncation
Section titled “String Truncation”Truncate strings with ellipsis.
function truncate(str, length, ending = '...') { if (str.length <= length) return str; return str.slice(0, length - ending.length) + ending;}
// Word-aware truncationfunction truncateWords(str, wordLimit, ending = '...') { const words = str.split(' '); if (words.length <= wordLimit) return str; return words.slice(0, wordLimit).join(' ') + ending;}
console.log(truncate('This is a long sentence', 10));// "This is..."
console.log(truncateWords('This is a very long sentence', 4));// "This is a very..."
Object Utilities
Section titled “Object Utilities”Deep Clone
Section titled “Deep Clone”Create deep copies of objects.
// Simple deep clone (limitations with functions, dates, etc.)function simpleDeepClone(obj) { return JSON.parse(JSON.stringify(obj));}
// More robust deep clonefunction deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (obj instanceof Array) return obj.map(item => deepClone(item)); if (obj instanceof Object) { const cloned = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key]); } } return cloned; }}
// Modern approach with structuredClone (where supported)const cloned = structuredClone(originalObject);
Object Merging
Section titled “Object Merging”Merge objects with different strategies.
// Shallow mergefunction shallowMerge(...objects) { return Object.assign({}, ...objects);}
// Deep mergefunction deepMerge(target, ...sources) { if (!sources.length) return target; const source = sources.shift();
if (isObject(target) && isObject(source)) { for (const key in source) { if (isObject(source[key])) { if (!target[key]) Object.assign(target, { [key]: {} }); deepMerge(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); } } }
return deepMerge(target, ...sources);}
function isObject(item) { return item && typeof item === 'object' && !Array.isArray(item);}
const obj1 = { a: 1, b: { c: 2 } };const obj2 = { b: { d: 3 }, e: 4 };const merged = deepMerge({}, obj1, obj2);// { a: 1, b: { c: 2, d: 3 }, e: 4 }
Async Utilities
Section titled “Async Utilities”Debouncing
Section titled “Debouncing”Delay function execution until after a pause in calls.
function debounce(func, delay) { let timeoutId; return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); };}
// Usage exampleconst searchInput = document.getElementById('search');const debouncedSearch = debounce((query) => { console.log('Searching for:', query); // Perform search API call}, 300);
searchInput.addEventListener('input', (e) => { debouncedSearch(e.target.value);});
Throttling
Section titled “Throttling”Limit function execution to at most once per specified interval.
function throttle(func, limit) { let inThrottle; return function (...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } };}
// Usage exampleconst throttledScroll = throttle(() => { console.log('Scroll event');}, 100);
window.addEventListener('scroll', throttledScroll);
Retry with Exponential Backoff
Section titled “Retry with Exponential Backoff”Retry failed operations with increasing delays.
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxRetries) throw error;
const delay = baseDelay * Math.pow(2, attempt); console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms`); await sleep(delay); } }}
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms));}
// Usageasync function fetchData() { const response = await fetch('/api/data'); if (!response.ok) throw new Error('Network error'); return response.json();}
retryWithBackoff(fetchData, 3, 1000) .then(data => console.log('Success:', data)) .catch(error => console.error('Failed after retries:', error));
Promise Utilities
Section titled “Promise Utilities”Useful Promise-based utilities.
// Timeout wrapperfunction withTimeout(promise, ms) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms) ); return Promise.race([promise, timeout]);}
// Parallel execution with concurrency limitasync function pMap(array, mapper, concurrency = Infinity) { const results = []; const executing = [];
for (const [index, item] of array.entries()) { const promise = Promise.resolve().then(() => mapper(item, index)); results.push(promise);
if (array.length >= concurrency) { executing.push(promise.then(() => executing.splice(executing.indexOf(promise), 1))); }
if (executing.length >= concurrency) { await Promise.race(executing); } }
return Promise.all(results);}
// Usageconst urls = ['url1', 'url2', 'url3', 'url4', 'url5'];const results = await pMap(urls, async (url) => { const response = await fetch(url); return response.json();}, 2); // Max 2 concurrent requests
DOM Utilities
Section titled “DOM Utilities”Element Creation
Section titled “Element Creation”Helper for creating DOM elements.
function createElement(tag, attributes = {}, children = []) { const element = document.createElement(tag);
// Set attributes Object.entries(attributes).forEach(([key, value]) => { if (key === 'className') { element.className = value; } else if (key === 'dataset') { Object.entries(value).forEach(([dataKey, dataValue]) => { element.dataset[dataKey] = dataValue; }); } else if (key.startsWith('on') && typeof value === 'function') { element.addEventListener(key.slice(2).toLowerCase(), value); } else { element.setAttribute(key, value); } });
// Add children children.forEach(child => { if (typeof child === 'string') { element.appendChild(document.createTextNode(child)); } else { element.appendChild(child); } });
return element;}
// Usageconst button = createElement('button', { className: 'btn btn-primary', dataset: { action: 'submit' }, onclick: () => console.log('Clicked!')}, ['Submit']);
document.body.appendChild(button);
Event Delegation
Section titled “Event Delegation”Efficient event handling for dynamic content.
function delegate(parent, selector, event, handler) { parent.addEventListener(event, (e) => { if (e.target.matches(selector)) { handler.call(e.target, e); } });}
// Usagedelegate(document.body, '.dynamic-button', 'click', function(e) { console.log('Button clicked:', this.textContent);});
Data Validation
Section titled “Data Validation”Form Validation
Section titled “Form Validation”Robust form validation utilities.
const validators = { required: (value) => value.trim() !== '', email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), minLength: (min) => (value) => value.length >= min, maxLength: (max) => (value) => value.length <= max, pattern: (regex) => (value) => regex.test(value), numeric: (value) => !isNaN(value) && !isNaN(parseFloat(value)), url: (value) => { try { new URL(value); return true; } catch { return false; } }};
function validateForm(data, rules) { const errors = {};
Object.entries(rules).forEach(([field, fieldRules]) => { const value = data[field] || ''; const fieldErrors = [];
fieldRules.forEach(rule => { if (typeof rule === 'function') { if (!rule(value)) fieldErrors.push('Invalid value'); } else if (typeof rule === 'object') { const { validator, message } = rule; if (!validator(value)) fieldErrors.push(message); } });
if (fieldErrors.length > 0) { errors[field] = fieldErrors; } });
return { isValid: Object.keys(errors).length === 0, errors };}
// Usageconst formData = { email: 'user@example.com', password: '123', website: 'https://example.com'};
const validationRules = { email: [ { validator: validators.required, message: 'Email is required' }, { validator: validators.email, message: 'Invalid email format' } ], password: [ { validator: validators.required, message: 'Password is required' }, { validator: validators.minLength(8), message: 'Password must be at least 8 characters' } ], website: [ { validator: validators.url, message: 'Invalid URL format' } ]};
const validation = validateForm(formData, validationRules);console.log(validation);
Performance Utilities
Section titled “Performance Utilities”Memoization
Section titled “Memoization”Cache function results for performance optimization.
function memoize(fn) { const cache = new Map();
return function (...args) { const key = JSON.stringify(args);
if (cache.has(key)) { return cache.get(key); }
const result = fn.apply(this, args); cache.set(key, result); return result; };}
// Usageconst expensiveFunction = memoize((n) => { console.log('Computing for', n); return n * n * n;});
console.log(expensiveFunction(5)); // Computes and cachesconsole.log(expensiveFunction(5)); // Returns from cache
Lazy Loading
Section titled “Lazy Loading”Load content only when needed.
class LazyLoader { constructor() { this.observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { this.loadElement(entry.target); this.observer.unobserve(entry.target); } }); }); }
observe(element) { this.observer.observe(element); }
loadElement(element) { if (element.dataset.src) { element.src = element.dataset.src; element.removeAttribute('data-src'); }
if (element.dataset.content) { element.innerHTML = element.dataset.content; element.removeAttribute('data-content'); }
element.classList.add('loaded'); }}
// Usageconst lazyLoader = new LazyLoader();document.querySelectorAll('[data-src], [data-content]').forEach((el) => { lazyLoader.observe(el);});
Random Utilities
Section titled “Random Utilities”Random Number Generators
Section titled “Random Number Generators”Various random number utilities.
// Random integer between min and max (inclusive)function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min;}
// Random float between min and maxfunction randomFloat(min, max) { return Math.random() * (max - min) + min;}
// Random element from arrayfunction randomChoice(array) { return array[Math.floor(Math.random() * array.length)];}
// Shuffle array (Fisher-Yates)function shuffle(array) { const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled;}
// Generate random stringfunction randomString(length, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { let result = ''; for (let i = 0; i < length; i++) { result += charset.charAt(Math.floor(Math.random() * charset.length)); } return result;}
console.log(randomInt(1, 10)); // Random integer 1-10console.log(randomChoice(['a', 'b', 'c'])); // Random elementconsole.log(shuffle([1, 2, 3, 4, 5])); // Shuffled arrayconsole.log(randomString(8)); // Random 8-character string
This cookbook provides practical solutions for common JavaScript tasks. Each recipe is production-ready and includes explanations of the approach used.