Skip to content

Commit 7b4ba72

Browse files
author
David Rodenas
committed
initial commit
0 parents  commit 7b4ba72

File tree

11 files changed

+3895
-0
lines changed

11 files changed

+3895
-0
lines changed

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
root = true
4+
5+
[*]
6+
end_of_line = lf
7+
insert_final_newline = true
8+
charset = utf-8
9+
indent_style = space
10+
indent_size = 2

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# See https://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
6+
# testing
7+
/coverage
8+
9+
# production
10+
/build
11+
12+
# misc
13+
.DS_Store
14+
.env.local
15+
.env.development.local
16+
.env.test.local
17+
.env.production.local
18+
19+
npm-debug.log*
20+
yarn-debug.log*
21+
yarn-error.log*

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
language: node_js
2+
node_js:
3+
- "node"
4+
- "8"

.vscode/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
// Set the default
3+
"editor.formatOnSave": false,
4+
// Enable per-language
5+
"[javascript]": {
6+
"editor.formatOnSave": true
7+
}
8+
}

LICENSE

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
ISC License
2+
3+
Copyright (c) 2018, David Rodenas
4+
5+
Permission to use, copy, modify, and/or distribute this software for any
6+
purpose with or without fee is hereby granted, provided that the above
7+
copyright notice and this permission notice appear in all copies.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

README.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
StringCacheMap ![building status](https://www.travis-ci.org/drpicox/string-cache-map.svg?branch=master)
2+
=============
3+
4+
StringCacheMap is a Map with an API compatible with
5+
[WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap)
6+
that stores string-any key-value pairs.
7+
8+
StringCacheMap has strings as keys and any value as a key. It is like using an object as a cache but with some advantages: 1) it is possible to limit the total of pairs key-value stored to a maximum amount, 2) default property names like "constructor" are not present, 3) it uses an additional victims cache to rescue old values and emulate a really fast LRU cache.
9+
10+
It is a substitute for WeakMap when keys are strings.
11+
12+
Quick Use
13+
----------
14+
15+
Install with `npm` or `yarn`:
16+
17+
```
18+
npm install string-cache-map
19+
```
20+
21+
Your Code:
22+
23+
```
24+
import StringCacheMap from 'string-cache-map'
25+
26+
const map = new StringCacheMap(2)
27+
map.set('a', 1)
28+
map.set('b', 2)
29+
30+
console.log(map.get('a')) // output 1
31+
console.log(map.has('a')) // output true
32+
```
33+
34+
StringCacheMap Basic API
35+
----------------------------
36+
37+
Add elements with `set` and `get` to obtain the value:
38+
39+
```
40+
import StringCacheMap from 'string-cache-map'
41+
42+
test('set values can be get', () => {
43+
const map = new StringCacheMap()
44+
map.set('fOf', 'foo')
45+
map.set('bOf', 'bar')
46+
47+
expect(map.get('fOf')).toBe('foo')
48+
expect(map.get('bOf')).toBe('bar')
49+
})
50+
```
51+
52+
Query if values are present with `has`:
53+
54+
```
55+
import StringCacheMap from 'string-cache-map'
56+
57+
test('has returns true if value is present, or false otherwise', () => {
58+
const map = new StringCacheMap()
59+
map.set('fOf', 'foo')
60+
map.set('uOf', undefined)
61+
62+
expect(map.has('fOf')).toBe(true)
63+
expect(map.has('uOf')).toBe(true)
64+
expect(map.has('bOf')).toBe(false)
65+
expect(map.has('constructor')).toBe(false)
66+
})
67+
```
68+
69+
Remove elements with `delete`:
70+
71+
```
72+
import StringCacheMap from 'string-cache-map'
73+
74+
test('delete removes a value and returns true if was removed, () => {
75+
const map = new StringCacheMap()
76+
map.set('fOf', 'foo')
77+
78+
expect(map.delete('fOf')).toBe(true)
79+
expect(map.delete('bOf')).toBe(false)
80+
expect(map.has('fOf')).toBe(false)
81+
})
82+
```
83+
84+
85+
Limit
86+
------
87+
88+
The first argument of the constructor is the limit:
89+
90+
```
91+
import StringCacheMap from 'string-cache-map'
92+
93+
test('capacity is up to the double of the limit', () => {
94+
const map = new StringCacheMap(2)
95+
map.set('a', 1)
96+
map.set('b', 2)
97+
98+
expect(map.get('a')).toBe(1)
99+
expect(map.has('a')).toBe(true)
100+
})
101+
```
102+
103+
It automatically deletes old unused values beyond the limit:
104+
105+
```
106+
import StringCacheMap from 'string-cache-map'
107+
108+
test('capacity ensured is the limit', () => {
109+
const map = new StringCacheMap(2)
110+
map.set('a', 1)
111+
map.set('b', 2)
112+
map.set('c', 3)
113+
map.set('d', 4)
114+
map.set('e', 5)
115+
116+
expect(map.get('d')).toBe(4)
117+
expect(map.has('a')).toBe(false)
118+
})
119+
```
120+
121+
LRU prioritizes last got values over last set values:
122+
123+
```
124+
import StringCacheMap from 'string-cache-map'
125+
126+
test('gets lru reprioritizes values', () => {
127+
const map = new StringCacheMap(2)
128+
map.set('a', 1)
129+
map.set('b', 2)
130+
map.set('c', 3)
131+
map.set('d', 4)
132+
map.get('a')
133+
map.get('b')
134+
map.get('c')
135+
map.set('e', 5)
136+
137+
expect(map.get('d')).toBeUndefined()
138+
expect(map.has('d')).toBe(false)
139+
})
140+
```
141+
142+
With hard mode off (second constructor argument set to false), LRU does not invalidate last set values:
143+
144+
```
145+
import StringCacheMap from 'string-cache-map'
146+
147+
test('gets lru does not expulse newer values if hard is false', () => {
148+
const map = new StringCacheMap(2, false)
149+
map.set('a', 1)
150+
map.set('b', 2)
151+
map.set('c', 3)
152+
map.set('d', 4)
153+
map.get('a')
154+
map.get('b')
155+
map.get('c')
156+
map.set('e', 5)
157+
158+
expect(map.get('d')).toBe(4)
159+
expect(map.has('d')).toBe(true)
160+
})
161+
```

__test__/StringCacheMap.spec.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const StringCacheMap = require('..')
2+
3+
describe('StringCacheMap', () => {
4+
test('StringCacheMap.length is 0', () => {
5+
expect(StringCacheMap.length).toBe(0)
6+
})
7+
8+
test('StringWeakMap must be use with new', () => {
9+
expect(StringCacheMap).toThrow()
10+
})
11+
12+
test('set values can be get', () => {
13+
const map = new StringCacheMap()
14+
map.set('fOf', 'foo')
15+
map.set('bOf', 'bar')
16+
17+
expect(map.get('fOf')).toBe('foo')
18+
expect(map.get('bOf')).toBe('bar')
19+
})
20+
21+
test('set calls can be chained', () => {
22+
const map = new StringCacheMap().set('fOf', 'foo').set('bOf', 'bar')
23+
24+
expect(map.get('fOf')).toBe('foo')
25+
expect(map.get('bOf')).toBe('bar')
26+
})
27+
28+
test('set can redefine existing values', () => {
29+
const map = new StringCacheMap().set('fOf', 'foo').set('bOf', 'bar')
30+
31+
map.set('bOf', 'baz')
32+
33+
expect(map.get('fOf')).toBe('foo')
34+
expect(map.get('bOf')).toBe('baz')
35+
})
36+
37+
test('get returns undefined if value is not setted', () => {
38+
const map = new StringCacheMap()
39+
40+
expect(map.get('fOf')).toBeUndefined()
41+
expect(map.get(window)).toBeUndefined()
42+
})
43+
44+
test('has returns true if value is present, or false otherwise', () => {
45+
const map = new StringCacheMap()
46+
map.set('fOf', 'foo')
47+
map.set('uOf', undefined)
48+
49+
expect(map.has('fOf')).toBe(true)
50+
expect(map.has('uOf')).toBe(true)
51+
expect(map.has('bOf')).toBe(false)
52+
expect(map.has('constructor')).toBe(false)
53+
expect(map.has(window)).toBe(false)
54+
})
55+
56+
test('delete removes a value and returns true if was removed, false otherwise', () => {
57+
const map = new StringCacheMap()
58+
map.set('fOf', 'foo')
59+
60+
expect(map.delete('fOf')).toBe(true)
61+
expect(map.delete('bOf')).toBe(false)
62+
expect(map.has('fOf')).toBe(false)
63+
})
64+
})

__test__/limits.spec.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
const StringCacheMap = require('..')
2+
3+
describe('StringCacheMap limits', () => {
4+
test('capacity is up to the double of the limit', () => {
5+
const map = new StringCacheMap(2)
6+
map.set('a', 1)
7+
map.set('b', 2)
8+
9+
expect(map.get('a')).toBe(1)
10+
expect(map.has('a')).toBe(true)
11+
})
12+
13+
test('capacity ensured is the limit', () => {
14+
const map = new StringCacheMap(2)
15+
map.set('a', 1)
16+
map.set('b', 2)
17+
map.set('c', 3)
18+
map.set('d', 4)
19+
map.set('e', 5)
20+
21+
expect(map.get('d')).toBe(4)
22+
expect(map.has('a')).toBe(false)
23+
})
24+
25+
test('acts an lru resquing elements present in victims', () => {
26+
const map = new StringCacheMap(2)
27+
map.set('a', 1)
28+
map.set('b', 2)
29+
map.set('c', 3)
30+
map.set('d', 4)
31+
map.get('a')
32+
map.set('e', 5)
33+
34+
expect(map.get('a')).toBe(1)
35+
expect(map.has('a')).toBe(true)
36+
})
37+
38+
test('gets lru may expulse newer values', () => {
39+
const map = new StringCacheMap(2)
40+
map.set('a', 1)
41+
map.set('b', 2)
42+
map.set('c', 3)
43+
map.set('d', 4)
44+
map.get('a')
45+
map.get('b')
46+
map.get('c')
47+
map.set('e', 5)
48+
49+
expect(map.get('d')).toBeUndefined()
50+
expect(map.has('d')).toBe(false)
51+
})
52+
53+
test('gets lru does not expulse newer values if hard is false', () => {
54+
const map = new StringCacheMap(2, false)
55+
map.set('a', 1)
56+
map.set('b', 2)
57+
map.set('c', 3)
58+
map.set('d', 4)
59+
map.get('a')
60+
map.get('b')
61+
map.get('c')
62+
map.set('e', 5)
63+
64+
expect(map.get('d')).toBe(4)
65+
expect(map.has('d')).toBe(true)
66+
})
67+
68+
test('delete also deletes from the victims', () => {
69+
const map = new StringCacheMap(2, false)
70+
map.set('a', 1)
71+
map.set('b', 2)
72+
map.set('c', 3)
73+
map.get('a')
74+
map.set('d', 4)
75+
map.delete('a')
76+
77+
expect(map.get('a')).toBeUndefined()
78+
expect(map.has('a')).toBe(false)
79+
})
80+
})

0 commit comments

Comments
 (0)