1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const TOMBSTONE = Symbol("tombstone");
|
9 | const UNDEFINED_MARKER = Symbol("undefined");
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | const extractPair = pair => {
|
28 | const key = pair[0];
|
29 | const val = pair[1];
|
30 | if (val === UNDEFINED_MARKER || val === TOMBSTONE) {
|
31 | return [key, undefined];
|
32 | } else {
|
33 | return (pair);
|
34 | }
|
35 | };
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | class StackedMap {
|
42 | |
43 |
|
44 |
|
45 | constructor(parentStack) {
|
46 |
|
47 | this.map = new Map();
|
48 |
|
49 | this.stack = parentStack === undefined ? [] : parentStack.slice();
|
50 | this.stack.push(this.map);
|
51 | }
|
52 |
|
53 | |
54 |
|
55 |
|
56 |
|
57 |
|
58 | set(item, value) {
|
59 | this.map.set(item, value === undefined ? UNDEFINED_MARKER : value);
|
60 | }
|
61 |
|
62 | |
63 |
|
64 |
|
65 |
|
66 | delete(item) {
|
67 | if (this.stack.length > 1) {
|
68 | this.map.set(item, TOMBSTONE);
|
69 | } else {
|
70 | this.map.delete(item);
|
71 | }
|
72 | }
|
73 |
|
74 | |
75 |
|
76 |
|
77 |
|
78 | has(item) {
|
79 | const topValue = this.map.get(item);
|
80 | if (topValue !== undefined) {
|
81 | return topValue !== TOMBSTONE;
|
82 | }
|
83 | if (this.stack.length > 1) {
|
84 | for (let i = this.stack.length - 2; i >= 0; i--) {
|
85 | const value = this.stack[i].get(item);
|
86 | if (value !== undefined) {
|
87 | this.map.set(item, value);
|
88 | return value !== TOMBSTONE;
|
89 | }
|
90 | }
|
91 | this.map.set(item, TOMBSTONE);
|
92 | }
|
93 | return false;
|
94 | }
|
95 |
|
96 | |
97 |
|
98 |
|
99 |
|
100 | get(item) {
|
101 | const topValue = this.map.get(item);
|
102 | if (topValue !== undefined) {
|
103 | return topValue === TOMBSTONE || topValue === UNDEFINED_MARKER
|
104 | ? undefined
|
105 | : topValue;
|
106 | }
|
107 | if (this.stack.length > 1) {
|
108 | for (let i = this.stack.length - 2; i >= 0; i--) {
|
109 | const value = this.stack[i].get(item);
|
110 | if (value !== undefined) {
|
111 | this.map.set(item, value);
|
112 | return value === TOMBSTONE || value === UNDEFINED_MARKER
|
113 | ? undefined
|
114 | : value;
|
115 | }
|
116 | }
|
117 | this.map.set(item, TOMBSTONE);
|
118 | }
|
119 | return undefined;
|
120 | }
|
121 |
|
122 | _compress() {
|
123 | if (this.stack.length === 1) return;
|
124 | this.map = new Map();
|
125 | for (const data of this.stack) {
|
126 | for (const pair of data) {
|
127 | if (pair[1] === TOMBSTONE) {
|
128 | this.map.delete(pair[0]);
|
129 | } else {
|
130 | this.map.set(pair[0], pair[1]);
|
131 | }
|
132 | }
|
133 | }
|
134 | this.stack = [this.map];
|
135 | }
|
136 |
|
137 | asArray() {
|
138 | this._compress();
|
139 | return Array.from(this.map.keys());
|
140 | }
|
141 |
|
142 | asSet() {
|
143 | this._compress();
|
144 | return new Set(this.map.keys());
|
145 | }
|
146 |
|
147 | asPairArray() {
|
148 | this._compress();
|
149 | return Array.from(this.map.entries(), extractPair);
|
150 | }
|
151 |
|
152 | asMap() {
|
153 | return new Map(this.asPairArray());
|
154 | }
|
155 |
|
156 | get size() {
|
157 | this._compress();
|
158 | return this.map.size;
|
159 | }
|
160 |
|
161 | createChild() {
|
162 | return new StackedMap(this.stack);
|
163 | }
|
164 | }
|
165 |
|
166 | module.exports = StackedMap;
|