🚀 ASF Ergonomics vs JavaScript

10 Examples Where ASF's Object Methods Are Simply Better

1. Filter Objects Naturally 🏆

ASF 3 Lines, Crystal Clear

Filter object properties like you would filter an array. Natural and intuitive.
let config = {
    darkMode: true, notifications: false,
    autoSave: true, analytics: false
};

let enabled = config.filter(fun(val) {
    return val == true;
});
Why ASF Wins: Objects are collections too! One method call, stays an object. No conversions needed.

JS 6 Lines, Mental Gymnastics

JavaScript makes you convert object → array → object. Every. Single. Time.
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
The Pain: Object → Array → Object dance. Destructuring syntax. Easy to forget fromEntries and end up with an array.

2. Transform All Values at Once 🎯

ASF Intuitive Transformation

Transform all values, keep all keys. As simple as array.map().
let prices = {
    laptop: 1200,
    mouse: 25,
    keyboard: 75
};

// Apply 20% discount to everything
let sale = prices.map(fun(price) {
    return price * 0.8;
});
Why ASF Wins: Works exactly like array.map(). Keys preserved automatically. One clean operation.

JS Manual Key Management

You must manually preserve keys in the returned array pairs. Easy to mess up.
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.
The Pain: Must manually return [key, value] pairs. One mistake = bugs. ASF does this automatically.

3. Validate All Properties Elegantly 💯

ASF Direct Validation

Check if all properties pass validation with access to both key and value.
let user = {
    username: 'alice',
    email: 'alice@example.com',
    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';
});
Why ASF Wins: .every() works on objects! Get both key and value. No conversions. Natural validation pattern.

JS Limited Access

Object.values() loses the keys. Need Object.entries() for key access. More verbose.
const user = {
    username: 'alice',
    email: 'alice@example.com',
    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?
The Pain: Object.values() = no keys. Object.entries() = destructuring everywhere. Pick your poison.

4. Find What You Need Fast 🔍

ASF Search by Key or Value

Check if any property matches condition. Get both key and value in callback.
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;
});
Why ASF Wins: Search by key name, value, or both. One method. Clean and powerful.

JS Always Need Entries

Want key access? You're using Object.entries(). Every time.
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
The Pain: Remember .entries() vs .values() vs .keys(). Destructure [key, val]. More to remember, more to type.

5. Loop Objects Like Arrays 🔄

ASF forEach Works Everywhere

Same forEach pattern for arrays and objects. Consistent and intuitive.
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!
Why ASF Wins: One forEach pattern for everything. Arrays, objects, strings. Consistency = easier to learn and remember.

JS Objects Are Second-Class

No object.forEach(). Must convert first. Arrays get forEach, objects don't.
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
The Pain: Objects don't have .forEach(). Always converting. Arrays are first-class, objects are not.

6. Smart Object Merging

ASF Elegant Solution

Merge objects with built-in method.
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 }
ASF Advantage: Built-in .merge() method modifies in place.

JS Standard Approach

JavaScript uses Object.assign() or spread operator.
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 }
JavaScript Note: Has Object.assign() and spread, but no .merge() method.

7. Check Size Naturally 📏

ASF Built-in Size Methods

Get property count or check if empty with dedicated methods.
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
Why ASF Wins: Dedicated methods. .size() and .isEmpty() are self-documenting. No tricks, no patterns to remember.

JS The Length Workaround

Objects don't have .length. Must count keys manually every time.
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
The Pain: Object.keys().length everywhere. No .isEmpty(). Array has .length, objects don't. Inconsistent.

8. Chain Operations Smoothly ⛓️

ASF Natural Method Chaining

Chain filter → map → filter. Object stays an object throughout.
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!
Why ASF Wins: Object methods return objects. Chain naturally. Read top-to-bottom. No conversion noise.

JS Conversion Chaos

Object → Array → Object → Array → Object. Conversions everywhere.
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.
The Pain: fromEntries/entries spaghetti. Type ping-pong. Bracket juggling. Unreadable mess.

9. Deep Clone Instantly 📋

ASF One Method Call

Deep copy nested objects with one simple method. Always works.
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'
Why ASF Wins: .clone() just works. Deep copies. No tricks. No limitations. Always reliable.

JS Three Bad Options

structuredClone is new. JSON breaks with functions. Spread is shallow.
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
The Pain: structuredClone is recent. JSON.parse/stringify has bugs. Spread isn't deep. No good answer until 2022.

10. Safe Defaults Built-in 🛡️

ASF .get() with Fallback

Get property with automatic fallback value. No undefined surprises.
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
Why ASF Wins: Built-in .get() method with defaults. No operators, no edge cases. Just works.

JS Operator Soup

Choose your operator: ??, ||, or manual checks. Each with gotchas.
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
The Pain: ?? vs || gotchas. No method, just operators. Need to remember which handles 0/''/false correctly.