Skip to content

Commit

Permalink
feat(lru): added lru helper
Browse files Browse the repository at this point in the history
  • Loading branch information
danrevah committed Feb 21, 2019
1 parent ed2d5df commit 1e5bce2
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 3 deletions.
19 changes: 19 additions & 0 deletions src/decorators/lru-memoize/lru-memoize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { generateFunctionDecorator, isUndefined } from '../../helpers/general';

export function LruMemoize(maxStorage: number, hashFn?: (...args: any[]) => string) {
return generateFunctionDecorator('LruMemoize', decorator);
}

function decorator(fn: (...args: any[]) => any, maxStorage: number, hashFn?: (...args: any[]) => string) {
const cache: { [key: string]: any } = {};

return function(...args: any[]) {
const hash = hashFn ? hashFn.apply(this, args) : JSON.stringify(args);

if (!isUndefined(cache[hash])) {
return cache[hash];
}

return (cache[hash] = fn.apply(this, args));
};
}
2 changes: 1 addition & 1 deletion src/decorators/memoize/memoize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class TestMemoize {
}
}

describe('Memoize', () => {
describe('Lru', () => {
it('should memoize values', () => {
const test = new TestMemoize();
expect(test.count()).toEqual(1);
Expand Down
2 changes: 1 addition & 1 deletion src/decorators/memoize/memoize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { generateFunctionDecorator, isUndefined } from '../../helpers';
import { generateFunctionDecorator, isUndefined } from '../../helpers/general';

export function Memoize(hashFn?: (...args: any[]) => string) {
return generateFunctionDecorator('Memoize', decorator, hashFn);
Expand Down
2 changes: 1 addition & 1 deletion src/helpers.spec.ts → src/helpers/general.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isObject, isUndefined } from './helpers';
import { isObject, isUndefined } from './general';

describe('Helpers', () => {
it('should check isUndefined', () => {
Expand Down
File renamed without changes.
36 changes: 36 additions & 0 deletions src/helpers/lru.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { LRU } from './lru';

describe('LRU', () => {
it('should check isUndefined', () => {
const lru = new LRU(3);

lru.insert('foo', 1);
lru.insert('bar', 2);
lru.insert('baz', 3);

expect(lru.toString()).toEqual('3, 2, 1');

lru.insert('foo', 1.5);

expect(lru.toString()).toEqual('1.5, 3, 2');

lru.insert('foo', 1);

expect(lru.toString()).toEqual('1, 3, 2');

lru.insert('foo', 1);
lru.insert('foo', 1);
lru.insert('foo', 1);

expect(lru.toString()).toEqual('1, 3, 2');

lru.insert('bar', 2.5);
lru.insert('bar', 2.5);

expect(lru.toString()).toEqual('2.5, 1, 3');

lru.insert('foobar', 4);

expect(lru.toString()).toEqual('4, 2.5, 1');
});
});
84 changes: 84 additions & 0 deletions src/helpers/lru.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { isUndefined } from './general';

interface Node<T> {
prev?: Node<T>
next?: Node<T>
value: T,
key: string
}

export class LRU<T> {
private totalStorage = 0;
private cache: { [key: string]: Node<T> } = {};
private head: Node<T> | undefined;
private tail: Node<T> | undefined;

constructor(private maxStorage: number) {}

get(key: string): T | undefined {
return !isUndefined(this.cache[key])
? this.cache[key].value
: undefined;
}

insert(key: string, value: T): void {
if (this.totalStorage >= this.maxStorage) {

if (!isUndefined(this.get(key))) {
this.remove(key);
} else {
const { key } = <Node<T>>this.tail;
this.remove(key);
}

}

const node: Node<T> = { key, value, next: this.head };
this.add(key, node);
}

remove(key: string): void {
const node = this.cache[key];

if (!node) return;

const { prev, next } = node;

if (prev) {
prev.next = next;
} else {
this.head = next;
}

if (node === this.tail) {
this.tail = prev || this.head;
}

if (next) {
next.prev = prev;
}

delete this.cache[key];
--this.totalStorage;
}

toString() {
const out: any[] = [];
for (let node = this.head; node; out.push(node.value), node = node.next);
return out.join(', ');
}

private add(key: string, node: Node<T>) {
if (this.head) {
this.head.prev = node;
}

if (!this.tail) {
this.tail = node;
}

this.cache[key] = this.head = node;

++this.totalStorage;
}
}

0 comments on commit 1e5bce2

Please sign in to comment.