AkashBabu/lib-lru-cache

View on GitHub
src/index.js

Summary

Maintainability
A
25 mins
Test Coverage
// Private properties
const dll = Symbol('dll');
const cache = Symbol('cache');
const head = Symbol('head');
const tail = Symbol('tail');
const len = Symbol('len');
const maxLen = Symbol('maxLen');

// Private methods
const makeHead = Symbol('makeHead');
const removeTail = Symbol('removeTail');

const REL = {
    PREV : 0,
    NEXT : 1,
};

export default class LRUCache {
    constructor(maxLength = 100) {
        if (maxLength < 2) {
            throw new Error(`You might not need cache for this maxLen: ${maxLength}!!`);
        }

        this[cache] = {};
        this[dll] = {};

        this[head] = null;
        this[tail] = null;
        this[len] = 0;
        this[maxLen] = maxLength;
    }

    [makeHead](key) {
        // Reducing operations if this is the head-key
        if (this[head] === key) return this[cache][key];

        const [prev, next] = this[dll][key];
        this[dll][next][REL.PREV] = prev;

        if (prev) {
            this[dll][prev][REL.NEXT] = next;
        } else {
            this[tail] = next;
        }

        // Setting This as the new Head
        this[dll][this[head]][REL.NEXT] = key;
        this[dll][key] = [this[head], null];
        this[head] = key;
    }

    [removeTail]() {
        const [_, next] = this[dll][this[tail]];
        this[dll][next][REL.PREV] = null;
        delete this[cache][this[tail]];
        delete this[dll][this[tail]];
        this[tail] = next;
        this[len]--;
    }

    get(key) {
        key += '';

        // Filtering unavailable keys
        if (!(key in this[cache])) return null;

        this[makeHead](key);
        return this[cache][key];
    }

    set(key, val) {
        key += '';

        if (key in this[cache]) {
            this[cache][key] = val;
            this[makeHead](key);
            return true;
        }

        // Add it into the Cache
        this[dll][key] = [this[head], null];
        this[cache][key] = val;
        this[len]++;

        /**
         * Below line is equivalent to:
         * this[head] && (this[dll][this[head]][REL.NEXT] = key);
         * this[head] = key;
         */
        this[head] = (this[dll][this[head]] || {})[REL.NEXT] = key;

        if (this[tail]) {
            // Remove last node if len is greater than maxLimit
            if (this[len] > this[maxLen]) {
                this[removeTail]();
            }
        } else {
            this[tail] = key;
        }
    }

    reset() {
        this[cache] = {};
        this[dll] = {};
        this[len] = 0;
        this[head] = this[tail] = null;
    }
}


if (require.main === module) {
    const lruCache = new LRUCache(3);
    lruCache.set('name1', 'test1');
    lruCache.set('name2', 'test2');
    lruCache.set('name3', 'test3');
    lruCache.set('name4', 'test4');

    lruCache.get('name4');
    lruCache.get('name3');
    lruCache.get('name2');
    lruCache.get('name1');
}