Explain Implement a Memoization Helper Function

Memoization is a programming technique that improves function performance by caching previously calculated results. When a function is called with the same arguments, instead of recalculating, it returns the cached result, significantly reducing execution time for expensive operations.

What is Memoization?

Memoization works by storing function results in a cache (usually an object or Map). When the function is called again with identical parameters, it checks the cache first. If the result exists, it returns the cached value; otherwise, it calculates the result and stores it for future use.

Basic Example: Slow Function Without Memoization

Let's start with a function that performs an expensive operation:

function slowAdd(a, b) {
    // Simulate expensive computation
    for (let i = 0; i < 100000; i++) {
        // Time-consuming loop
    }
    return a + b;
}

console.time("First call");
console.log(slowAdd(5, 4));
console.timeEnd("First call");

console.time("Second call");
console.log(slowAdd(5, 4));
console.timeEnd("Second call");
9
First call: 2.156ms
9
Second call: 1.987ms

Both calls take similar time because the function recalculates the result each time.

Creating a Memoization Helper Function

Here's a generic memoization helper that can wrap any function:

function memoize(func) {
    const cache = {};
    
    return function(...args) {
        // Create a unique key from arguments
        const key = JSON.stringify(args);
        
        // Check if result is already cached
        if (cache[key]) {
            console.log("Cache hit!");
            return cache[key];
        }
        
        // Calculate and cache the result
        console.log("Computing...");
        const result = func.apply(this, args);
        cache[key] = result;
        return result;
    };
}

function expensiveAdd(a, b) {
    for (let i = 0; i < 1000000; i++) {}
    return a + b;
}

const memoizedAdd = memoize(expensiveAdd);

console.time("First call");
console.log(memoizedAdd(10, 5));
console.timeEnd("First call");

console.time("Second call");
console.log(memoizedAdd(10, 5));
console.timeEnd("Second call");

console.time("Third call");
console.log(memoizedAdd(10, 5));
console.timeEnd("Third call");
Computing...
15
First call: 3.245ms
Cache hit!
15
Second call: 0.123ms
Cache hit!
15
Third call: 0.089ms

Real-World Example: Fibonacci Sequence

Fibonacci calculation is a perfect example where memoization provides dramatic performance improvements:

// Without memoization - exponential time complexity
function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// With memoization - linear time complexity
const memoizedFibonacci = memoize(function(n) {
    if (n <= 1) return n;
    return memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2);
});

console.time("Regular Fibonacci(35)");
console.log("Result:", fibonacci(35));
console.timeEnd("Regular Fibonacci(35)");

console.time("Memoized Fibonacci(35)");
console.log("Result:", memoizedFibonacci(35));
console.timeEnd("Memoized Fibonacci(35)");
Result: 9227465
Regular Fibonacci(35): 45.892ms
Computing...
Computing...
Computing...
Result: 9227465
Memoized Fibonacci(35): 0.234ms

Advanced Memoization with Custom Key Generation

For complex objects or specific use cases, you might need custom key generation:

function memoizeWithCustomKey(func, keyGenerator) {
    const cache = new Map();
    
    return function(...args) {
        const key = keyGenerator ? keyGenerator(...args) : JSON.stringify(args);
        
        if (cache.has(key)) {
            return cache.get(key);
        }
        
        const result = func.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

// Custom key for user objects
const calculateUserScore = memoizeWithCustomKey(
    function(user, multiplier) {
        console.log(`Calculating score for ${user.name}`);
        return user.points * multiplier;
    },
    (user, multiplier) => `${user.id}-${multiplier}` // Custom key
);

const user1 = { id: 1, name: "Alice", points: 100 };
const user2 = { id: 1, name: "Alice Updated", points: 100 }; // Same ID

console.log(calculateUserScore(user1, 2));
console.log(calculateUserScore(user2, 2)); // Uses cache despite name change
Calculating score for Alice
200
200

When to Use Memoization

Good For Avoid For
Pure functions (same input ? same output) Functions with side effects
Expensive computations Simple, fast operations
Recursive algorithms Functions called once
Functions called repeatedly Functions with large result sets

Conclusion

Memoization is a powerful optimization technique that trades memory for speed by caching function results. It's particularly effective for expensive pure functions and recursive algorithms, providing dramatic performance improvements with minimal code changes.

Updated on: 2026-03-15T23:19:01+05:30

442 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements