Search code examples
javascriptmethodsmethod-chaining

How to Implement Method Chaining


Is there a way to chain the following methods from Cache class

cache = cache.getKey(key) || cache.setKey(key).get(key); // cache = cache.getKey(key) || cache.setKey(key).getKey(key);

I know we have native methods Map.prototype​.set() and Map.prototype​.get() but I want to implement it this way. Let me know if you have any suggestions.

function isObject(arg) {
  const typeOfObj = typeof arg;
  return (typeOfObj === 'object' || typeOfObj === 'function') && arg !== null;
}

class Cache {
  constructor() {
    this.map = new Map();
    this.weakmap = new WeakMap();
  }

  setKey(key) {
    const map = this[isObject(key) ? 'weakmap' : 'map'];
    return map.set(key, new Cache());
  }

  getKey(key) {
    const map = this[isObject(key) ? 'weakmap' : 'map'];
    return map.get(key);
  }
}

function getCache(args, cache) {
  for (const key of args) {
    cache = cache.getKey(key) || cache.setKey(key).get(key);
    // cache = cache.getKey(key) || cache.setKey(key).getKey(key);
  }
  return cache;
}

function memoize(fn) {
  const cache = new Cache();
  return (...args) => {
    const item = getCache(args, cache);

    if (Reflect.has(item, 'value')) {
      return item.value;
    }

    return (item.value = fn(args));
  };
}

let counter = 1;
function foo() {
  counter += 1;
  return counter;
}

const id1 = Symbol('id');
const id2 = Symbol('id');

const memoizedFoo = memoize(foo);
console.log(memoizedFoo(id1)); // 2
console.log(memoizedFoo(id1)); // 2
console.log(memoizedFoo(id2)); // 3
console.log(memoizedFoo(id2)); // 3

Any help is greatly appreciated.


Solution

  • Unfortunately, if you're trying to "GET" a key, you cannot return the map, as you're asking for the key, so it must return the key. So you can't chain that method. But any method that shouldn't return something by default (IE would be void), we can do this:

    cache = cache.getKey(key) || cache.setKey(key).get(key);

    This will return a boolean, as the || symbol means return true if either is true.

    But if you'd like to do the sort of chaining that JQuery does, that's easily achievable! What you want to do is return this or the object in every method.

    Like so:

    function isObject(arg) {
      const typeOfObj = typeof arg;
      return (typeOfObj === 'object' || typeOfObj === 'function') && arg !== null;
    }
    
    class Cache {
      constructor() {
        this.map = new Map();
        this.weakmap = new WeakMap();
      }
    
      setKey(key) {
        const map = this[isObject(key) ? 'weakmap' : 'map'];
        map.set(key, new Cache());
        return this; // HERE'S THE IMPORTANT PART
      }
    }
    
    let counter = 1;
    function foo() {
      counter += 1;
      return counter;
    }
    
    const id1 = Symbol('id');
    const id2 = Symbol('id');
    
    const memoizedFoo = memoize(foo);
    console.log(memoizedFoo(id1)); // 2
    console.log(memoizedFoo(id1)); // 2
    console.log(memoizedFoo(id2)); // 3
    console.log(memoizedFoo(id2)); // 3