@naturalcycles/js-lib 15.45.0 → 15.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env.d.ts +6 -0
- package/dist/env.js +8 -0
- package/dist/object/keySortedMap.d.ts +18 -14
- package/dist/object/keySortedMap.js +74 -56
- package/dist/object/keySortedMap2.d.ts +72 -0
- package/dist/object/keySortedMap2.js +184 -0
- package/dist/object/map2.d.ts +1 -0
- package/dist/object/map2.js +3 -0
- package/dist/object/set2.d.ts +1 -1
- package/dist/object/set2.js +2 -2
- package/dist/string/stringify.js +45 -43
- package/package.json +2 -2
- package/src/env.ts +9 -0
- package/src/object/keySortedMap.ts +84 -59
- package/src/object/keySortedMap2.ts +221 -0
- package/src/object/map2.ts +4 -0
- package/src/object/set2.ts +2 -2
- package/src/string/stringify.ts +54 -52
package/dist/env.d.ts
CHANGED
|
@@ -12,3 +12,9 @@ export declare function isServerSide(): boolean;
|
|
|
12
12
|
* Will return `false` in Node.js.
|
|
13
13
|
*/
|
|
14
14
|
export declare function isClientSide(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Almost the same as isServerSide()
|
|
17
|
+
* (isServerSide should return true for Node),
|
|
18
|
+
* but detects Node specifically (not Deno, not Bun, etc).
|
|
19
|
+
*/
|
|
20
|
+
export declare function isNode(): boolean;
|
package/dist/env.js
CHANGED
|
@@ -17,3 +17,11 @@ export function isClientSide() {
|
|
|
17
17
|
// oxlint-disable-next-line unicorn/prefer-global-this
|
|
18
18
|
return typeof window !== 'undefined' && !!window?.document;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Almost the same as isServerSide()
|
|
22
|
+
* (isServerSide should return true for Node),
|
|
23
|
+
* but detects Node specifically (not Deno, not Bun, etc).
|
|
24
|
+
*/
|
|
25
|
+
export function isNode() {
|
|
26
|
+
return typeof process !== 'undefined' && process?.release?.name === 'node';
|
|
27
|
+
}
|
|
@@ -20,9 +20,8 @@ export interface KeySortedMapOptions<K> {
|
|
|
20
20
|
* @experimental
|
|
21
21
|
*/
|
|
22
22
|
export declare class KeySortedMap<K, V> implements Map<K, V> {
|
|
23
|
-
|
|
23
|
+
#private;
|
|
24
24
|
private readonly map;
|
|
25
|
-
private readonly sortedKeys;
|
|
26
25
|
constructor(entries?: [K, V][], opt?: KeySortedMapOptions<K>);
|
|
27
26
|
/**
|
|
28
27
|
* Convenience way to create KeySortedMap from object.
|
|
@@ -52,25 +51,30 @@ export declare class KeySortedMap<K, V> implements Map<K, V> {
|
|
|
52
51
|
values(): MapIterator<V>;
|
|
53
52
|
entries(): MapIterator<[K, V]>;
|
|
54
53
|
[Symbol.iterator](): MapIterator<[K, V]>;
|
|
54
|
+
toString(): string;
|
|
55
55
|
[Symbol.toStringTag]: string;
|
|
56
56
|
/**
|
|
57
57
|
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
58
58
|
*/
|
|
59
59
|
forEach(cb: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
lastEntry(): [K, V]
|
|
60
|
+
firstKeyOrUndefined(): K | undefined;
|
|
61
|
+
firstKey(): K;
|
|
62
|
+
lastKeyOrUndefined(): K | undefined;
|
|
63
|
+
lastKey(): K;
|
|
64
|
+
firstValueOrUndefined(): V | undefined;
|
|
65
|
+
firstValue(): V;
|
|
66
|
+
lastValueOrUndefined(): V | undefined;
|
|
67
|
+
lastValue(): V;
|
|
68
|
+
firstEntryOrUndefined(): [K, V] | undefined;
|
|
69
|
+
firstEntry(): [K, V];
|
|
70
|
+
lastEntryOrUndefined(): [K, V] | undefined;
|
|
71
|
+
lastEntry(): [K, V];
|
|
72
72
|
toJSON(): Record<string, V>;
|
|
73
73
|
toObject(): Record<string, V>;
|
|
74
|
+
/**
|
|
75
|
+
* Clones the KeySortedMap into ordinary Map.
|
|
76
|
+
*/
|
|
77
|
+
toMap(): Map<K, V>;
|
|
74
78
|
/**
|
|
75
79
|
* lowerBound: first index i s.t. keys[i] >= target
|
|
76
80
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { _assert } from '../error/index.js';
|
|
1
2
|
/**
|
|
2
3
|
* Maintains sorted array of keys.
|
|
3
4
|
* Sorts **on insertion**, not on retrieval.
|
|
@@ -10,15 +11,15 @@
|
|
|
10
11
|
* @experimental
|
|
11
12
|
*/
|
|
12
13
|
export class KeySortedMap {
|
|
13
|
-
opt;
|
|
14
14
|
map;
|
|
15
|
-
sortedKeys;
|
|
15
|
+
#sortedKeys;
|
|
16
16
|
constructor(entries = [], opt = {}) {
|
|
17
|
-
this
|
|
17
|
+
this.#comparator = opt.comparator;
|
|
18
18
|
this.map = new Map(entries);
|
|
19
|
-
this
|
|
19
|
+
this.#sortedKeys = [...this.map.keys()];
|
|
20
20
|
this.sortKeys();
|
|
21
21
|
}
|
|
22
|
+
#comparator;
|
|
22
23
|
/**
|
|
23
24
|
* Convenience way to create KeySortedMap from object.
|
|
24
25
|
*/
|
|
@@ -30,7 +31,7 @@ export class KeySortedMap {
|
|
|
30
31
|
}
|
|
31
32
|
clear() {
|
|
32
33
|
this.map.clear();
|
|
33
|
-
this
|
|
34
|
+
this.#sortedKeys.length = 0;
|
|
34
35
|
}
|
|
35
36
|
has(key) {
|
|
36
37
|
return this.map.has(key);
|
|
@@ -44,7 +45,7 @@ export class KeySortedMap {
|
|
|
44
45
|
setMany(obj) {
|
|
45
46
|
for (const [k, v] of Object.entries(obj)) {
|
|
46
47
|
this.map.set(k, v);
|
|
47
|
-
this
|
|
48
|
+
this.#sortedKeys.push(k);
|
|
48
49
|
}
|
|
49
50
|
// Resort all at once
|
|
50
51
|
this.sortKeys();
|
|
@@ -63,7 +64,7 @@ export class KeySortedMap {
|
|
|
63
64
|
// Find insertion index (lower_bound).
|
|
64
65
|
const i = this.lowerBound(key);
|
|
65
66
|
// Only insert into keys when actually new.
|
|
66
|
-
this
|
|
67
|
+
this.#sortedKeys.splice(i, 0, key);
|
|
67
68
|
this.map.set(key, value);
|
|
68
69
|
return this;
|
|
69
70
|
}
|
|
@@ -77,15 +78,15 @@ export class KeySortedMap {
|
|
|
77
78
|
// Remove from keys using binary search to avoid O(n) find.
|
|
78
79
|
const i = this.lowerBound(key);
|
|
79
80
|
// Because key existed, it must be at i.
|
|
80
|
-
if (i < this
|
|
81
|
-
this
|
|
81
|
+
if (i < this.#sortedKeys.length && this.#sortedKeys[i] === key) {
|
|
82
|
+
this.#sortedKeys.splice(i, 1);
|
|
82
83
|
}
|
|
83
84
|
else {
|
|
84
85
|
// Extremely unlikely if external mutation happened; safe guard.
|
|
85
86
|
// Fall back to linear search (shouldn't happen).
|
|
86
|
-
const j = this
|
|
87
|
+
const j = this.#sortedKeys.indexOf(key);
|
|
87
88
|
if (j !== -1)
|
|
88
|
-
this
|
|
89
|
+
this.#sortedKeys.splice(j, 1);
|
|
89
90
|
}
|
|
90
91
|
return true;
|
|
91
92
|
}
|
|
@@ -93,94 +94,111 @@ export class KeySortedMap {
|
|
|
93
94
|
* Iterables (Map-compatible), all in sorted order.
|
|
94
95
|
*/
|
|
95
96
|
*keys() {
|
|
96
|
-
for (
|
|
97
|
-
yield
|
|
97
|
+
for (const key of this.#sortedKeys) {
|
|
98
|
+
yield key;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
*values() {
|
|
101
|
-
for (
|
|
102
|
-
yield this.map.get(
|
|
102
|
+
for (const key of this.#sortedKeys) {
|
|
103
|
+
yield this.map.get(key);
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
*entries() {
|
|
106
|
-
for (
|
|
107
|
-
const k = this.sortedKeys[i];
|
|
107
|
+
for (const k of this.#sortedKeys) {
|
|
108
108
|
yield [k, this.map.get(k)];
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
[Symbol.iterator]() {
|
|
112
112
|
return this.entries();
|
|
113
113
|
}
|
|
114
|
+
toString() {
|
|
115
|
+
console.log('toString called !!!!!!!!!!!!!!!!!!!!!');
|
|
116
|
+
return 'abc';
|
|
117
|
+
}
|
|
114
118
|
[Symbol.toStringTag] = 'KeySortedMap';
|
|
115
119
|
/**
|
|
116
120
|
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
117
121
|
*/
|
|
118
122
|
forEach(cb, thisArg) {
|
|
119
|
-
const
|
|
120
|
-
for (
|
|
121
|
-
|
|
122
|
-
cb.call(thisArg, m.get(k), k, this);
|
|
123
|
+
const { map } = this;
|
|
124
|
+
for (const k of this.#sortedKeys) {
|
|
125
|
+
cb.call(thisArg, map.get(k), k, this);
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* These allocate; use iterators/forEach for maximum performance.
|
|
128
|
-
*/
|
|
129
|
-
keysArray() {
|
|
130
|
-
return this.sortedKeys.slice();
|
|
131
|
-
}
|
|
132
|
-
valuesArray() {
|
|
133
|
-
// oxlint-disable-next-line unicorn/no-new-array
|
|
134
|
-
const a = Array(this.sortedKeys.length);
|
|
135
|
-
for (let i = 0; i < this.sortedKeys.length; i++) {
|
|
136
|
-
a[i] = this.map.get(this.sortedKeys[i]);
|
|
137
|
-
}
|
|
138
|
-
return a;
|
|
139
|
-
}
|
|
140
|
-
entriesArray() {
|
|
141
|
-
// oxlint-disable-next-line unicorn/no-new-array
|
|
142
|
-
const out = Array(this.sortedKeys.length);
|
|
143
|
-
for (let i = 0; i < this.sortedKeys.length; i++) {
|
|
144
|
-
const k = this.sortedKeys[i];
|
|
145
|
-
out[i] = [k, this.map.get(k)];
|
|
146
|
-
}
|
|
147
|
-
return out;
|
|
128
|
+
firstKeyOrUndefined() {
|
|
129
|
+
return this.#sortedKeys[0];
|
|
148
130
|
}
|
|
149
|
-
/** Fast helpers */
|
|
150
131
|
firstKey() {
|
|
151
|
-
|
|
132
|
+
_assert(this.#sortedKeys.length, 'Map.firstKey called on empty map');
|
|
133
|
+
return this.#sortedKeys[0];
|
|
134
|
+
}
|
|
135
|
+
lastKeyOrUndefined() {
|
|
136
|
+
return this.#sortedKeys.length ? this.#sortedKeys[this.#sortedKeys.length - 1] : undefined;
|
|
152
137
|
}
|
|
153
138
|
lastKey() {
|
|
154
|
-
|
|
139
|
+
_assert(this.#sortedKeys.length, 'Map.lastKey called on empty map');
|
|
140
|
+
return this.#sortedKeys[this.#sortedKeys.length - 1];
|
|
141
|
+
}
|
|
142
|
+
firstValueOrUndefined() {
|
|
143
|
+
return this.map.get(this.#sortedKeys[0]);
|
|
144
|
+
}
|
|
145
|
+
firstValue() {
|
|
146
|
+
_assert(this.#sortedKeys.length, 'Map.firstValue called on empty map');
|
|
147
|
+
return this.map.get(this.#sortedKeys[0]);
|
|
148
|
+
}
|
|
149
|
+
lastValueOrUndefined() {
|
|
150
|
+
return this.#sortedKeys.length
|
|
151
|
+
? this.map.get(this.#sortedKeys[this.#sortedKeys.length - 1])
|
|
152
|
+
: undefined;
|
|
153
|
+
}
|
|
154
|
+
lastValue() {
|
|
155
|
+
_assert(this.#sortedKeys.length, 'Map.lastValue called on empty map');
|
|
156
|
+
return this.map.get(this.#sortedKeys[this.#sortedKeys.length - 1]);
|
|
157
|
+
}
|
|
158
|
+
firstEntryOrUndefined() {
|
|
159
|
+
if (!this.#sortedKeys.length)
|
|
160
|
+
return;
|
|
161
|
+
const k = this.#sortedKeys[0];
|
|
162
|
+
return [k, this.map.get(k)];
|
|
155
163
|
}
|
|
156
164
|
firstEntry() {
|
|
157
|
-
|
|
165
|
+
_assert(this.#sortedKeys.length, 'Map.firstEntry called on empty map');
|
|
166
|
+
const k = this.#sortedKeys[0];
|
|
167
|
+
return [k, this.map.get(k)];
|
|
168
|
+
}
|
|
169
|
+
lastEntryOrUndefined() {
|
|
170
|
+
if (!this.#sortedKeys.length)
|
|
158
171
|
return;
|
|
159
|
-
const k = this
|
|
172
|
+
const k = this.#sortedKeys[this.#sortedKeys.length - 1];
|
|
160
173
|
return [k, this.map.get(k)];
|
|
161
174
|
}
|
|
162
175
|
lastEntry() {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const k = this.sortedKeys[this.sortedKeys.length - 1];
|
|
176
|
+
_assert(this.#sortedKeys.length, 'Map.lastEntry called on empty map');
|
|
177
|
+
const k = this.#sortedKeys[this.#sortedKeys.length - 1];
|
|
166
178
|
return [k, this.map.get(k)];
|
|
167
179
|
}
|
|
168
180
|
toJSON() {
|
|
169
181
|
return this.toObject();
|
|
170
182
|
}
|
|
171
183
|
toObject() {
|
|
172
|
-
return Object.fromEntries(this.
|
|
184
|
+
return Object.fromEntries(this.entries());
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Clones the KeySortedMap into ordinary Map.
|
|
188
|
+
*/
|
|
189
|
+
toMap() {
|
|
190
|
+
return new Map(this.entries());
|
|
173
191
|
}
|
|
174
192
|
/**
|
|
175
193
|
* lowerBound: first index i s.t. keys[i] >= target
|
|
176
194
|
*/
|
|
177
195
|
lowerBound(target) {
|
|
178
196
|
let lo = 0;
|
|
179
|
-
let hi = this
|
|
197
|
+
let hi = this.#sortedKeys.length;
|
|
180
198
|
while (lo < hi) {
|
|
181
199
|
// oxlint-disable-next-line no-bitwise
|
|
182
200
|
const mid = (lo + hi) >>> 1;
|
|
183
|
-
if (this
|
|
201
|
+
if (this.#sortedKeys[mid] < target) {
|
|
184
202
|
lo = mid + 1;
|
|
185
203
|
}
|
|
186
204
|
else {
|
|
@@ -190,6 +208,6 @@ export class KeySortedMap {
|
|
|
190
208
|
return lo;
|
|
191
209
|
}
|
|
192
210
|
sortKeys() {
|
|
193
|
-
this
|
|
211
|
+
this.#sortedKeys.sort(this.#comparator);
|
|
194
212
|
}
|
|
195
213
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Comparator } from '../types.js';
|
|
2
|
+
export interface KeySortedMapOptions<K> {
|
|
3
|
+
/**
|
|
4
|
+
* Defaults to undefined.
|
|
5
|
+
* Undefined (default comparator) works well for String keys.
|
|
6
|
+
* For Number keys - use comparators.numericAsc (or desc),
|
|
7
|
+
* otherwise sorting will be wrong (lexicographic).
|
|
8
|
+
*/
|
|
9
|
+
comparator?: Comparator<K>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Maintains sorted array of keys.
|
|
13
|
+
* Sorts **data access**, not on insertion.
|
|
14
|
+
*
|
|
15
|
+
* @experimental
|
|
16
|
+
*/
|
|
17
|
+
export declare class KeySortedMap2<K, V> implements Map<K, V> {
|
|
18
|
+
#private;
|
|
19
|
+
private readonly map;
|
|
20
|
+
private readonly maybeSortedKeys;
|
|
21
|
+
private keysAreSorted;
|
|
22
|
+
constructor(entries?: [K, V][], opt?: KeySortedMapOptions<K>);
|
|
23
|
+
/**
|
|
24
|
+
* Convenience way to create KeySortedMap from object.
|
|
25
|
+
*/
|
|
26
|
+
static of<V>(obj: Record<any, V>): KeySortedMap2<string, V>;
|
|
27
|
+
get size(): number;
|
|
28
|
+
clear(): void;
|
|
29
|
+
has(key: K): boolean;
|
|
30
|
+
get(key: K): V | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Allows to set multiple key-value pairs at once.
|
|
33
|
+
*/
|
|
34
|
+
setMany(obj: Record<any, V>): this;
|
|
35
|
+
/**
|
|
36
|
+
* Insert or update. Keeps keys array sorted at all times.
|
|
37
|
+
* Returns this (Map-like).
|
|
38
|
+
*/
|
|
39
|
+
set(key: K, value: V): this;
|
|
40
|
+
/**
|
|
41
|
+
* Delete by key. Returns boolean like Map.delete.
|
|
42
|
+
*/
|
|
43
|
+
delete(key: K): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Iterables (Map-compatible), all in sorted order.
|
|
46
|
+
*/
|
|
47
|
+
keys(): MapIterator<K>;
|
|
48
|
+
values(): MapIterator<V>;
|
|
49
|
+
entries(): MapIterator<[K, V]>;
|
|
50
|
+
[Symbol.iterator](): MapIterator<[K, V]>;
|
|
51
|
+
[Symbol.toStringTag]: string;
|
|
52
|
+
/**
|
|
53
|
+
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
54
|
+
*/
|
|
55
|
+
forEach(cb: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
|
|
56
|
+
firstKeyOrUndefined(): K | undefined;
|
|
57
|
+
firstKey(): K;
|
|
58
|
+
lastKeyOrUndefined(): K | undefined;
|
|
59
|
+
lastKey(): K;
|
|
60
|
+
firstValueOrUndefined(): V | undefined;
|
|
61
|
+
firstValue(): V;
|
|
62
|
+
lastValueOrUndefined(): V | undefined;
|
|
63
|
+
lastValue(): V;
|
|
64
|
+
firstEntryOrUndefined(): [K, V] | undefined;
|
|
65
|
+
firstEntry(): [K, V];
|
|
66
|
+
lastEntryOrUndefined(): [K, V] | undefined;
|
|
67
|
+
lastEntry(): [K, V];
|
|
68
|
+
toJSON(): Record<string, V>;
|
|
69
|
+
toObject(): Record<string, V>;
|
|
70
|
+
private getSortedKeys;
|
|
71
|
+
private sortKeys;
|
|
72
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { _assert } from '../error/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Maintains sorted array of keys.
|
|
4
|
+
* Sorts **data access**, not on insertion.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export class KeySortedMap2 {
|
|
9
|
+
map;
|
|
10
|
+
maybeSortedKeys;
|
|
11
|
+
keysAreSorted = false;
|
|
12
|
+
constructor(entries = [], opt = {}) {
|
|
13
|
+
this.#comparator = opt.comparator;
|
|
14
|
+
this.map = new Map(entries);
|
|
15
|
+
this.maybeSortedKeys = [...this.map.keys()];
|
|
16
|
+
}
|
|
17
|
+
#comparator;
|
|
18
|
+
/**
|
|
19
|
+
* Convenience way to create KeySortedMap from object.
|
|
20
|
+
*/
|
|
21
|
+
static of(obj) {
|
|
22
|
+
return new KeySortedMap2(Object.entries(obj));
|
|
23
|
+
}
|
|
24
|
+
get size() {
|
|
25
|
+
return this.map.size;
|
|
26
|
+
}
|
|
27
|
+
clear() {
|
|
28
|
+
this.map.clear();
|
|
29
|
+
this.maybeSortedKeys.length = 0;
|
|
30
|
+
this.keysAreSorted = true;
|
|
31
|
+
}
|
|
32
|
+
has(key) {
|
|
33
|
+
return this.map.has(key);
|
|
34
|
+
}
|
|
35
|
+
get(key) {
|
|
36
|
+
return this.map.get(key);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Allows to set multiple key-value pairs at once.
|
|
40
|
+
*/
|
|
41
|
+
setMany(obj) {
|
|
42
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
43
|
+
this.map.set(k, v);
|
|
44
|
+
this.maybeSortedKeys.push(k);
|
|
45
|
+
}
|
|
46
|
+
this.keysAreSorted = false;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Insert or update. Keeps keys array sorted at all times.
|
|
51
|
+
* Returns this (Map-like).
|
|
52
|
+
*/
|
|
53
|
+
set(key, value) {
|
|
54
|
+
if (!this.map.has(key)) {
|
|
55
|
+
this.maybeSortedKeys.push(key);
|
|
56
|
+
this.keysAreSorted = false;
|
|
57
|
+
}
|
|
58
|
+
this.map.set(key, value);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Delete by key. Returns boolean like Map.delete.
|
|
63
|
+
*/
|
|
64
|
+
delete(key) {
|
|
65
|
+
if (!this.map.has(key))
|
|
66
|
+
return false;
|
|
67
|
+
this.map.delete(key);
|
|
68
|
+
// Delete operation keeps the array **as-is**, it may have been sorted or not.
|
|
69
|
+
const j = this.maybeSortedKeys.indexOf(key);
|
|
70
|
+
if (j !== -1)
|
|
71
|
+
this.maybeSortedKeys.splice(j, 1);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Iterables (Map-compatible), all in sorted order.
|
|
76
|
+
*/
|
|
77
|
+
*keys() {
|
|
78
|
+
for (const key of this.getSortedKeys()) {
|
|
79
|
+
yield key;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
*values() {
|
|
83
|
+
for (const key of this.getSortedKeys()) {
|
|
84
|
+
yield this.map.get(key);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
*entries() {
|
|
88
|
+
for (const k of this.getSortedKeys()) {
|
|
89
|
+
yield [k, this.map.get(k)];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
[Symbol.iterator]() {
|
|
93
|
+
return this.entries();
|
|
94
|
+
}
|
|
95
|
+
[Symbol.toStringTag] = 'KeySortedMap';
|
|
96
|
+
/**
|
|
97
|
+
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
98
|
+
*/
|
|
99
|
+
forEach(cb, thisArg) {
|
|
100
|
+
const { map } = this;
|
|
101
|
+
for (const k of this.getSortedKeys()) {
|
|
102
|
+
cb.call(thisArg, map.get(k), k, this);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
firstKeyOrUndefined() {
|
|
106
|
+
return this.getSortedKeys()[0];
|
|
107
|
+
}
|
|
108
|
+
firstKey() {
|
|
109
|
+
_assert(this.maybeSortedKeys.length, 'Map.firstKey called on empty map');
|
|
110
|
+
return this.getSortedKeys()[0];
|
|
111
|
+
}
|
|
112
|
+
lastKeyOrUndefined() {
|
|
113
|
+
if (!this.maybeSortedKeys.length)
|
|
114
|
+
return;
|
|
115
|
+
const keys = this.getSortedKeys();
|
|
116
|
+
return keys[keys.length - 1];
|
|
117
|
+
}
|
|
118
|
+
lastKey() {
|
|
119
|
+
const k = this.lastKeyOrUndefined();
|
|
120
|
+
_assert(k, 'Map.lastKey called on empty map');
|
|
121
|
+
return k;
|
|
122
|
+
}
|
|
123
|
+
firstValueOrUndefined() {
|
|
124
|
+
if (!this.maybeSortedKeys.length)
|
|
125
|
+
return;
|
|
126
|
+
return this.map.get(this.getSortedKeys()[0]);
|
|
127
|
+
}
|
|
128
|
+
firstValue() {
|
|
129
|
+
const v = this.firstValueOrUndefined();
|
|
130
|
+
_assert(v, 'Map.firstValue called on empty map');
|
|
131
|
+
return v;
|
|
132
|
+
}
|
|
133
|
+
lastValueOrUndefined() {
|
|
134
|
+
if (!this.maybeSortedKeys.length)
|
|
135
|
+
return;
|
|
136
|
+
const keys = this.getSortedKeys();
|
|
137
|
+
return this.map.get(keys[keys.length - 1]);
|
|
138
|
+
}
|
|
139
|
+
lastValue() {
|
|
140
|
+
const v = this.lastValueOrUndefined();
|
|
141
|
+
_assert(v, 'Map.lastValue called on empty map');
|
|
142
|
+
return v;
|
|
143
|
+
}
|
|
144
|
+
firstEntryOrUndefined() {
|
|
145
|
+
if (!this.maybeSortedKeys.length)
|
|
146
|
+
return;
|
|
147
|
+
const k = this.getSortedKeys()[0];
|
|
148
|
+
return [k, this.map.get(k)];
|
|
149
|
+
}
|
|
150
|
+
firstEntry() {
|
|
151
|
+
const e = this.firstEntryOrUndefined();
|
|
152
|
+
_assert(e, 'Map.firstEntry called on empty map');
|
|
153
|
+
return e;
|
|
154
|
+
}
|
|
155
|
+
lastEntryOrUndefined() {
|
|
156
|
+
if (!this.maybeSortedKeys.length)
|
|
157
|
+
return;
|
|
158
|
+
const keys = this.getSortedKeys();
|
|
159
|
+
const k = keys[keys.length - 1];
|
|
160
|
+
return [k, this.map.get(k)];
|
|
161
|
+
}
|
|
162
|
+
lastEntry() {
|
|
163
|
+
const e = this.firstEntryOrUndefined();
|
|
164
|
+
_assert(e, 'Map.lastEntry called on empty map');
|
|
165
|
+
return e;
|
|
166
|
+
}
|
|
167
|
+
toJSON() {
|
|
168
|
+
return this.toObject();
|
|
169
|
+
}
|
|
170
|
+
toObject() {
|
|
171
|
+
return Object.fromEntries(this.entries());
|
|
172
|
+
}
|
|
173
|
+
getSortedKeys() {
|
|
174
|
+
if (!this.keysAreSorted) {
|
|
175
|
+
return this.sortKeys();
|
|
176
|
+
}
|
|
177
|
+
return this.maybeSortedKeys;
|
|
178
|
+
}
|
|
179
|
+
sortKeys() {
|
|
180
|
+
this.maybeSortedKeys.sort(this.#comparator);
|
|
181
|
+
this.keysAreSorted = true;
|
|
182
|
+
return this.maybeSortedKeys;
|
|
183
|
+
}
|
|
184
|
+
}
|
package/dist/object/map2.d.ts
CHANGED
package/dist/object/map2.js
CHANGED
package/dist/object/set2.d.ts
CHANGED