10 Examples Where ASF's Object Methods Are Simply Better
let config = { darkMode: true, notifications: false, autoSave: true, analytics: false }; let enabled = config.filter(fun(val) { return val == true; });
const config = { darkMode: true, notifications: false, autoSave: true, analytics: false }; const enabled = Object.fromEntries( Object.entries(config) .filter(([key, val]) => val === true) ); // Must remember to destructure [key, val] // Must remember Object.fromEntries at the end
let prices = { laptop: 1200, mouse: 25, keyboard: 75 }; // Apply 20% discount to everything let sale = prices.map(fun(price) { return price * 0.8; });
const prices = { laptop: 1200, mouse: 25, keyboard: 75 }; // Remember: must return [key, value] pairs! const sale = Object.fromEntries( Object.entries(prices) .map(([key, price]) => [key, price * 0.8]) ); // Forgot to preserve key? Broken. // Forgot fromEntries? You get an array.
let user = { username: 'alice', email: '[email protected]', age: 25 }; // Check all fields present and valid let valid = user.every(fun(val, key) { return val != null && val != ''; }); // Can also validate by key name! let hasRequired = user.every(fun(val, key) { return key == 'age' || typeof val == 'string'; });
const user = {
username: 'alice',
email: '[email protected]',
age: 25
};
// Loses key information!
const valid = Object.values(user)
.every(val => val != null && val !== '');
// Need entries for key access
const hasRequired = Object.entries(user)
.every(([key, val]) =>
key === 'age' || typeof val === 'string'
);
// Choose: lose keys or use verbose syntax?
let features = { darkMode: false, analytics: false, logging: true, debugging: true }; // Any feature starting with 'log' enabled? let hasLogging = features.some(fun(val, key) { return val && key.startsWith('log'); }); // Any feature enabled at all? let anyEnabled = features.some(fun(val) { return val == true; });
const features = { darkMode: false, analytics: false, logging: true, debugging: true }; // Need that [key, val] destructuring again... const hasLogging = Object.entries(features) .some(([key, val]) => val && key.startsWith('log') ); // At least this one's simpler const anyEnabled = Object.values(features) .some(val => val === true); // Different patterns for different needs
let scores = { math: 85, english: 92, science: 78 }; // Process each property scores.forEach(fun(val, key) { print(key + ': ' + val); }); // Works just like array.forEach() // Get value, key, and even the object itself!
const scores = { math: 85, english: 92, science: 78 }; // No scores.forEach() - objects aren't iterable // Must convert to array first Object.entries(scores).forEach(([key, val]) => { console.log(key + ': ' + val); }); // Or use for...in (but that has prototype issues) // Or use Object.keys() then loop... // Multiple ways, none direct
let defaults = { timeout: 30, retry: 3, verbose: false }; let userConfig = { timeout: 60, cache: true }; defaults.merge(userConfig); // Result: // { timeout: 60, retry: 3, verbose: false, cache: true }
const defaults = { timeout: 30, retry: 3, verbose: false }; const userConfig = { timeout: 60, cache: true }; Object.assign(defaults, userConfig); // or: const merged = { ...defaults, ...userConfig }; // Result: // { timeout: 60, retry: 3, verbose: false, cache: true }
let config = { a: 1, b: 2, c: 3 }; let count = config.size(); // 3 let empty = config.isEmpty(); // false config.clear(); empty = config.isEmpty(); // true // Readable, discoverable, obvious // Works like .length for arrays
const config = { a: 1, b: 2, c: 3 }; const count = Object.keys(config).length; const empty = Object.keys(config).length === 0; // Or with newer syntax const empty2 = Object.keys(config).length === 0; // Always: convert to array → get length // Every. Single. Time. // No .isEmpty(), must compare === 0
let products = { p1: { name: 'Widget', cost: 10, stock: 15 }, p2: { name: 'Gadget', cost: 25, stock: 0 }, p3: { name: 'Tool', cost: 30, stock: 8 } }; let result = products .filter(fun(item) { return item.stock > 0; }) .map(fun(item) { return { name: item.name, price: item.cost * 1.5 }; }) .filter(fun(item) { return item.price > 30; }); // Clean pipeline. Object → Object → Object // No type conversions!
const products = { p1: { name: 'Widget', cost: 10, stock: 15 }, p2: { name: 'Gadget', cost: 25, stock: 0 }, p3: { name: 'Tool', cost: 30, stock: 8 } }; const result = Object.fromEntries( // Array → Object Object.entries( // Object → Array Object.fromEntries( // Array → Object Object.entries(products) // Object → Array .filter(([k, item]) => item.stock > 0) .map(([k, item]) => [k, { name: item.name, price: item.cost * 1.5 }]) ) ).filter(([k, item]) => item.price > 30) ); // Nested conversions. Hard to read. Easy to mess up.
let user = { name: 'John', scores: [85, 90], address: { city: 'Boston' } }; let copy = user.clone(); // Modify copy copy.name = 'Jane'; copy.scores[1] = 95; copy.address.city = 'NYC'; // Original untouched! // user.name = 'John' // user.address.city = 'Boston'
const user = { name: 'John', scores: [85, 90], address: { city: 'Boston' } }; // Option 1: Modern but new (2022+) const copy1 = structuredClone(user); // Option 2: JSON trick (breaks with functions, dates, etc) const copy2 = JSON.parse(JSON.stringify(user)); // Option 3: Spread (SHALLOW only!) const copy3 = { ...user }; copy3.address.city = 'NYC'; // OOPS! user.address.city is now 'NYC' too! // No simple, universal solution
let config = { timeout: 30, retry: 3 }; // Property exists → return its value let timeout = config.get('timeout', 60); // 30 // Property missing → return default let port = config.get('port', 8080); // 8080 // Clean, safe, readable // No ?? or || operators needed
const config = { timeout: 30, retry: 3 }; // Nullish coalescing (newer, better) let port = config.port ?? 8080; // Logical OR (watch out for 0 and '') let port2 = config.port || 8080; // Manual check (verbose) let port3 = config.port !== undefined ? config.port : 8080; // ?? vs || behave differently! // 0, '', false with || → use default // Only null/undefined with ?? → use default