-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathObjectsMap.ts
More file actions
136 lines (99 loc) · 3.64 KB
/
ObjectsMap.ts
File metadata and controls
136 lines (99 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { fail } from '@noeldemartin/utils/helpers/error_helpers';
import { toString } from './object_helpers';
export type ObjectKeyExtractor<T> = (item: T) => string;
export default class ObjectsMap<Item extends object> {
/* eslint-disable max-len */
public static createFromArray<T extends object>(
items: Iterable<T>,
keyExtractor?: ObjectKeyExtractor<T>
): ObjectsMap<T>;
public static createFromArray<T extends object, S extends keyof T>(items: Iterable<T>, key: S): ObjectsMap<T>;
/* eslint-enable max-len */
public static createFromArray<T extends object, S extends keyof T>(
items: Iterable<T>,
key?: S | ObjectKeyExtractor<T>,
): ObjectsMap<T> {
const keyExtractor = typeof key === 'string' ? (item: T) => item[key] : key;
const map = new ObjectsMap(keyExtractor as ObjectKeyExtractor<T>);
for (const item of items) {
map.add(item);
}
return map;
}
private keysToItems: Map<string, Item>;
private itemsToKeys: WeakMap<Item, string>;
private getKey: (item: Item) => string;
constructor(keyExtractor?: ObjectKeyExtractor<Item>) {
this.keysToItems = new Map();
this.itemsToKeys = new WeakMap();
this.getKey = keyExtractor ?? ((item) => toString(item));
}
public *[Symbol.iterator](): Iterator<[string, Item]> {
yield* this.keysToItems;
}
public get size(): number {
return this.keysToItems.size;
}
public items(): IterableIterator<Item> {
return this.keysToItems.values();
}
public keys(): IterableIterator<string> {
return this.keysToItems.keys();
}
public entries(): IterableIterator<[string, Item]> {
return this.keysToItems.entries();
}
public add(item: Item): void {
const key = this.getKey(item);
this.keysToItems.set(key, item);
this.itemsToKeys.set(item, key);
}
public require(key: string): Item {
return this.keysToItems.get(key) ?? fail(`Couldn't find item with '${key}' key`);
}
public get(key: string): Item | undefined {
return this.keysToItems.get(key);
}
public getKeys(): string[] {
return [...this.keysToItems.keys()];
}
public getItems(): Item[] {
return [...this.keysToItems.values()];
}
public hasKey(key: string): boolean {
return this.keysToItems.has(key);
}
public hasItem(item: Item): boolean {
return this.itemsToKeys.has(item);
}
public delete(item: Item): void {
const key = this.itemsToKeys.get(item);
if (!key) return;
this.itemsToKeys.delete(item);
this.keysToItems.delete(key);
}
public filter(condition: (item: Item, key: string) => boolean): this {
const filteredMap = (this.constructor as typeof ObjectsMap).createFromArray([], this.getKey);
for (const [key, item] of this.entries()) {
if (!condition(item, key)) {
continue;
}
filteredMap.add(item);
}
return filteredMap as this;
}
public clear(): void {
this.itemsToKeys = new WeakMap();
this.keysToItems.clear();
}
public clone<T extends ObjectsMap<Item>>(this: T): T {
const ThisConstructor = this.constructor as { new (keyExtractor: (item: Item) => string): T };
const copy = new ThisConstructor(this.getKey);
copy.keysToItems = new Map(this.keysToItems);
copy.itemsToKeys = new WeakMap();
for (const [key, item] of copy.keysToItems.entries()) {
copy.itemsToKeys.set(item, key);
}
return copy;
}
}