@fluid-experimental/property-common 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.4.1.0.148229

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.
Files changed (127) hide show
  1. package/.eslintrc.js +9 -11
  2. package/.mocharc.json +6 -6
  3. package/README.md +1 -0
  4. package/api-extractor.json +2 -2
  5. package/dist/chronometer.d.ts.map +1 -1
  6. package/dist/chronometer.js.map +1 -1
  7. package/dist/consoleUtils.d.ts.map +1 -1
  8. package/dist/consoleUtils.js +4 -1
  9. package/dist/consoleUtils.js.map +1 -1
  10. package/dist/constants.d.ts +6 -6
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/constants.js +16 -26
  13. package/dist/constants.js.map +1 -1
  14. package/dist/datastructures/collection.d.ts.map +1 -1
  15. package/dist/datastructures/collection.js +5 -4
  16. package/dist/datastructures/collection.js.map +1 -1
  17. package/dist/datastructures/dataArray.d.ts.map +1 -1
  18. package/dist/datastructures/dataArray.js +18 -9
  19. package/dist/datastructures/dataArray.js.map +1 -1
  20. package/dist/datastructures/integer64.d.ts.map +1 -1
  21. package/dist/datastructures/integer64.js +1 -1
  22. package/dist/datastructures/integer64.js.map +1 -1
  23. package/dist/datastructures/sortedCollection.d.ts.map +1 -1
  24. package/dist/datastructures/sortedCollection.js +3 -1
  25. package/dist/datastructures/sortedCollection.js.map +1 -1
  26. package/dist/deferredPromise.d.ts +1 -1
  27. package/dist/deferredPromise.d.ts.map +1 -1
  28. package/dist/deferredPromise.js +9 -3
  29. package/dist/deferredPromise.js.map +1 -1
  30. package/dist/deterministicRandomGenerator.d.ts.map +1 -1
  31. package/dist/deterministicRandomGenerator.js +6 -6
  32. package/dist/deterministicRandomGenerator.js.map +1 -1
  33. package/dist/error_objects/flaggedError.d.ts.map +1 -1
  34. package/dist/error_objects/flaggedError.js.map +1 -1
  35. package/dist/error_objects/httpError.d.ts.map +1 -1
  36. package/dist/error_objects/httpError.js +9 -9
  37. package/dist/error_objects/httpError.js.map +1 -1
  38. package/dist/error_objects/httpErrorNoStack.d.ts.map +1 -1
  39. package/dist/error_objects/httpErrorNoStack.js.map +1 -1
  40. package/dist/error_objects/operationError.d.ts +7 -7
  41. package/dist/error_objects/operationError.d.ts.map +1 -1
  42. package/dist/error_objects/operationError.js +7 -7
  43. package/dist/error_objects/operationError.js.map +1 -1
  44. package/dist/guidUtils.d.ts.map +1 -1
  45. package/dist/guidUtils.js +10 -9
  46. package/dist/guidUtils.js.map +1 -1
  47. package/dist/hashCalculator.d.ts.map +1 -1
  48. package/dist/hashCalculator.js.map +1 -1
  49. package/dist/joinPaths.d.ts.map +1 -1
  50. package/dist/joinPaths.js.map +1 -1
  51. package/dist/packageVersion.d.ts +1 -1
  52. package/dist/packageVersion.js +1 -1
  53. package/dist/packageVersion.js.map +1 -1
  54. package/lib/chronometer.d.ts.map +1 -1
  55. package/lib/chronometer.js.map +1 -1
  56. package/lib/consoleUtils.d.ts.map +1 -1
  57. package/lib/consoleUtils.js +4 -1
  58. package/lib/consoleUtils.js.map +1 -1
  59. package/lib/constants.d.ts +6 -6
  60. package/lib/constants.d.ts.map +1 -1
  61. package/lib/constants.js +16 -26
  62. package/lib/constants.js.map +1 -1
  63. package/lib/datastructures/collection.d.ts.map +1 -1
  64. package/lib/datastructures/collection.js +5 -4
  65. package/lib/datastructures/collection.js.map +1 -1
  66. package/lib/datastructures/dataArray.d.ts.map +1 -1
  67. package/lib/datastructures/dataArray.js +18 -9
  68. package/lib/datastructures/dataArray.js.map +1 -1
  69. package/lib/datastructures/integer64.d.ts.map +1 -1
  70. package/lib/datastructures/integer64.js +1 -1
  71. package/lib/datastructures/integer64.js.map +1 -1
  72. package/lib/datastructures/sortedCollection.d.ts.map +1 -1
  73. package/lib/datastructures/sortedCollection.js +3 -1
  74. package/lib/datastructures/sortedCollection.js.map +1 -1
  75. package/lib/deferredPromise.d.ts +1 -1
  76. package/lib/deferredPromise.d.ts.map +1 -1
  77. package/lib/deferredPromise.js +9 -3
  78. package/lib/deferredPromise.js.map +1 -1
  79. package/lib/deterministicRandomGenerator.d.ts.map +1 -1
  80. package/lib/deterministicRandomGenerator.js +6 -6
  81. package/lib/deterministicRandomGenerator.js.map +1 -1
  82. package/lib/error_objects/flaggedError.d.ts.map +1 -1
  83. package/lib/error_objects/flaggedError.js.map +1 -1
  84. package/lib/error_objects/httpError.d.ts.map +1 -1
  85. package/lib/error_objects/httpError.js +9 -9
  86. package/lib/error_objects/httpError.js.map +1 -1
  87. package/lib/error_objects/httpErrorNoStack.d.ts.map +1 -1
  88. package/lib/error_objects/httpErrorNoStack.js.map +1 -1
  89. package/lib/error_objects/operationError.d.ts +7 -7
  90. package/lib/error_objects/operationError.d.ts.map +1 -1
  91. package/lib/error_objects/operationError.js +7 -7
  92. package/lib/error_objects/operationError.js.map +1 -1
  93. package/lib/guidUtils.d.ts.map +1 -1
  94. package/lib/guidUtils.js +10 -9
  95. package/lib/guidUtils.js.map +1 -1
  96. package/lib/hashCalculator.d.ts.map +1 -1
  97. package/lib/hashCalculator.js.map +1 -1
  98. package/lib/joinPaths.d.ts.map +1 -1
  99. package/lib/joinPaths.js.map +1 -1
  100. package/lib/packageVersion.d.ts +1 -1
  101. package/lib/packageVersion.js +1 -1
  102. package/lib/packageVersion.js.map +1 -1
  103. package/package.json +42 -40
  104. package/platform-dependent/README.md +1 -0
  105. package/platform-dependent/browser.js +5 -5
  106. package/platform-dependent/package.json +14 -14
  107. package/platform-dependent/server.js +5 -5
  108. package/prettier.config.cjs +1 -1
  109. package/src/chronometer.ts +174 -169
  110. package/src/consoleUtils.ts +16 -11
  111. package/src/constants.ts +1683 -1640
  112. package/src/datastructures/collection.ts +417 -416
  113. package/src/datastructures/dataArray.ts +516 -492
  114. package/src/datastructures/integer64.ts +102 -99
  115. package/src/datastructures/sortedCollection.ts +155 -145
  116. package/src/deferredPromise.ts +43 -42
  117. package/src/deterministicRandomGenerator.ts +59 -59
  118. package/src/error_objects/flaggedError.ts +29 -29
  119. package/src/error_objects/httpError.ts +45 -42
  120. package/src/error_objects/httpErrorNoStack.ts +17 -17
  121. package/src/error_objects/operationError.ts +62 -62
  122. package/src/guidUtils.ts +182 -171
  123. package/src/hashCalculator.ts +5 -2
  124. package/src/joinPaths.ts +15 -9
  125. package/src/packageVersion.ts +1 -1
  126. package/tsconfig.esnext.json +6 -6
  127. package/tsconfig.json +9 -13
@@ -9,423 +9,424 @@
9
9
  import _ from "lodash";
10
10
 
11
11
  const MSGS = {
12
- TYPE_MISMATCH: "Type does not match this collection type",
13
- KEY_ALREADY_EXISTS: "Collection key already exists. ",
14
- KEY_DOES_NOT_EXIST: "Collection key does not exist in this collection.",
15
- MUST_GIVE_KEY: "Collection missing key.",
16
- MUST_GIVE_VALUE: "Collection missing value.",
17
- KEY_NOT_VALID: "Key must be of type String or Number",
12
+ TYPE_MISMATCH: "Type does not match this collection type",
13
+ KEY_ALREADY_EXISTS: "Collection key already exists. ",
14
+ KEY_DOES_NOT_EXIST: "Collection key does not exist in this collection.",
15
+ MUST_GIVE_KEY: "Collection missing key.",
16
+ MUST_GIVE_VALUE: "Collection missing value.",
17
+ KEY_NOT_VALID: "Key must be of type String or Number",
18
18
  };
19
19
 
20
20
  export class Collection<T> {
21
- protected _items: { [key: string]: T; } = {};
22
- protected _order: (string | number)[] = [];
23
-
24
- /**
25
- * @param _name - a friendly name to describe this collection. If undefined
26
- * the collection will have a default "Untitled Collection" assigned to its name.
27
- * @param _type - optional parameter pointing to the constructor
28
- * of a type this Collection will host.
29
- */
30
- constructor(protected _name = "Untitled Collection", protected _type?: new () => any) {
31
- }
32
-
33
- // Pass-thru binding handles
34
- onAdd(in_key, in_value) { }
35
- onRemove(in_key, in_value) { }
36
- onClear(in_items) { }
37
-
38
- public get items() {
39
- return this._items;
40
- }
41
-
42
- public get keys() {
43
- return Object.keys(this._items);
44
- }
45
-
46
- /**
47
- * @param in_key - Key to store the value under
48
- * @param in_value - Value to store in the collection
49
- * @returns Return the value passed in
50
- */
51
- add(in_key: number | string, in_value: T): T {
52
- this._checkType(in_value);
53
- this._checkIsNewKey(in_key);
54
-
55
- this._items[in_key] = in_value;
56
- this._order.push(in_key);
57
-
58
- this.onAdd(in_key, in_value);
59
-
60
- return in_value;
61
- }
62
-
63
- /**
64
- * Checks if in_value's type is equal to this Collection type. If this collection
65
- * has no type set, the check will pass.
66
- *
67
- * @param in_value - A value that is equal to the type managed by this collection.
68
- * @returns Return true if the type is a valid type for this
69
- * collection, throw otherwise.
70
- */
71
- private _checkType(in_value: T) {
72
- if (this._type && !(in_value instanceof this._type)) {
73
- throw new Error(MSGS.TYPE_MISMATCH);
74
- } else {
75
- return true;
76
- }
77
- }
78
-
79
- /**
80
- * Bulk add items.
81
- * @param in_items - List of key-value pairs to be stored in the collection
82
- * @returns this collection after add
83
- */
84
- bulkAdd(in_items: { [key: string]: T; }) {
85
- _.each(in_items, (item, key) => {
86
- this.add(key, item);
87
- });
88
- return this;
89
- }
90
-
91
- /**
92
- * Bulk remove items.
93
- * @param in_items - List of key-value items to be removed
94
- * @returns this collection after add
95
- */
96
- bulkRemove(in_items: { [key: string]: T; }) {
97
- _.each(in_items, (item, key) => {
98
- this.remove(key);
99
- });
100
-
101
- return this;
102
- }
103
-
104
- /**
105
- * Test if this collection is empty
106
- * @returns true if empty, false otherwise
107
- * */
108
- isEmpty() {
109
- return _.isEmpty(this._items);
110
- }
111
-
112
- /**
113
- * Return the first item in the collection, null if empty
114
- * @returns first item, or undefined if empty
115
- * */
116
- getFirstItem(): T | undefined {
117
- const index = _.first(this._order);
118
- return index === undefined ? index : this._items[index];
119
- }
120
-
121
- /**
122
- * Return the last item in the collection, null if empty
123
- * @returns - last item, or undefined if empty
124
- * */
125
- getLastItem(): T | undefined {
126
- const index = _.last(this._order);
127
- return index === undefined ? index : this._items[index];
128
- }
129
-
130
- /**
131
- * @returns Returns the type of collection (Array, etc.)
132
- */
133
- getType() {
134
- return this._type;
135
- }
136
-
137
- /**
138
- * @returns Returns the name of the collection
139
- */
140
- getName(): string {
141
- return this._name;
142
- }
143
-
144
- /**
145
- * Filter out by function
146
- * @param in_filterFunction - with arguments key and item
147
- * @returns A new filtered collection
148
- */
149
- filter(in_filterFunction: (key: string, item: T) => boolean): Collection<T> {
150
- const rtn = new Collection<T>();
151
-
152
- const filterCb = function(in_key, in_item) {
153
- const keeper = in_filterFunction(in_key, in_item);
154
- if (keeper) {
155
- rtn.add(in_key, in_item);
156
- }
157
- };
158
-
159
- this.iterate(filterCb);
160
-
161
- return rtn;
162
- }
163
-
164
- /**
165
- * Filter out all keys NOT matching the in_filterKey
166
- * @param in_filterKey - a single key or an array of keys, if the
167
- * item matches any of the keys it will be filtered in.
168
- * @returns New filtered collection with all the items
169
- * matching at least one key.
170
- */
171
- filterByKey(in_filterKey: string | string[]): Collection<T> {
172
- const rtn = new Collection<T>();
173
-
174
- const filterCb = _.isArray(in_filterKey)
175
- ? function(in_key, in_item) {
176
- if (in_filterKey.includes(in_key)) {
177
- rtn.add(in_key, in_item);
178
- }
179
- } : function(in_key, in_item) {
180
- if (in_key === in_filterKey) {
181
- rtn.add(in_key, in_item);
182
- }
183
- };
184
-
185
- this.iterate(filterCb);
186
-
187
- return rtn;
188
- }
189
-
190
- /**
191
- * Filter out all keys NOT matching the in_filterValue
192
- * @param in_filterValue - Value to filter on
193
- * @returns Return a filtered collection
194
- */
195
- filterByValue(in_filterValue: T): Collection<T> {
196
- const rtn = new Collection<T>();
197
-
198
- const filterCb = function(in_key, in_item) {
199
- if (in_item === in_filterValue) {
200
- rtn.add(in_key, in_item);
201
- }
202
- };
203
-
204
- this.iterate(filterCb);
205
-
206
- return rtn;
207
- }
208
-
209
- /**
210
- * Remove an item from this Collection. This method returns a Boolean indicating
211
- * the success or failure of the removal. This is practical because if we were
212
- * to throw an error when the key doesn't exist, it would require additional
213
- * checks by the caller to make sure this key exists prior to removal. Which
214
- * would make the attempt of removal more verbose and also costly because the
215
- * caller would have to keep a list – somewhere else – of the things he can
216
- * and cannot remove.
217
- *
218
- * @param in_key - the key we wish to remove
219
- * @returns true if the key exists and was removed, false otherwise.
220
- */
221
- remove(in_key: number | string): boolean {
222
- if (!this.has(in_key)) {
223
- return false;
224
- }
225
-
226
- const remember = this._items[in_key];
227
-
228
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
229
- delete this._items[in_key];
230
- this._order.splice(this._order.indexOf(in_key), 1);
231
-
232
- this.onRemove(in_key, remember);
233
-
234
- return true;
235
- }
236
-
237
- /**
238
- * Return the number of items in this Collection
239
- * @returns the number of items in the collection
240
- */
241
- getCount(): number {
242
- return this._order.length;
243
- }
244
-
245
- /**
246
- * Returns this collection as an ordered Array
247
- * @returns Array including the values
248
- */
249
- getAsArray() {
250
- const rtnArr: T[] = new Array(this.getCount());
251
-
252
- for (let i = 0; i < this._order.length; i++) {
253
- rtnArr[i] = this._items[this._order[i]];
254
- }
255
-
256
- return rtnArr;
257
- }
258
-
259
- /**
260
- * @param in_key - the key we are looking for
261
- * @returns true if the item exists
262
- */
263
- has(in_key: string | number): boolean {
264
- return Object.prototype.hasOwnProperty.call(this._items, in_key);
265
- }
266
-
267
- /**
268
- * Return an item associated with the given key
269
- * @param in_key - the key for the item in this
270
- * Collection
271
- * @returns The item
272
- */
273
- item(in_key: number | string) {
274
- return this._items[in_key];
275
- }
276
-
277
- /**
278
- * Checks if this is a new key in the collection. Throw if already exists.
279
- * @param in_key - The key to check against
280
- * @returns true if key is new
281
- */
282
- private _checkIsNewKey(in_key: number | string) {
283
- if (this.has(in_key)) {
284
- throw new Error(`${MSGS.KEY_ALREADY_EXISTS} ${in_key}`);
285
- }
286
-
287
- return true;
288
- }
289
-
290
- /**
291
- * Checks if the key exists in the collection. Throw if not.
292
- * @param in_key - the key to check against
293
- * @returns true if key exists
294
- */
295
- private _checkKeyExists(in_key: number | string) {
296
- if (!this.has(in_key)) {
297
- throw new Error(`${MSGS.KEY_DOES_NOT_EXIST} ${in_key}`);
298
- }
299
-
300
- return true;
301
- }
302
-
303
- /**
304
- * Set an existing key to a value. If key doesn't exist this call will throw
305
- * an error.
306
- * @param in_key - The key we want to modify
307
- * @param in_value - The value we are to set at this key
308
- * @returns The value passed in
309
- */
310
- set(in_key: string, in_value: T) {
311
- this._checkKeyExists(in_key);
312
-
313
- this._items[in_key] = in_value;
314
-
315
- return in_value;
316
- }
317
-
318
- /**
319
- * Iterate over this collection and run the callback with passing the key and
320
- * item in the iteration loop.
321
- * @param in_callback - A function that we will call on each item
322
- * of this collection. If the callback returns false then the iteration will exit early.
323
- */
324
- iterate(in_callback) {
325
- for (const key of this._order) {
326
- const continu = in_callback(key, this._items[key]);
327
- if (continu === false) {
328
- break;
329
- }
330
- }
331
- }
332
-
333
- /**
334
- * Iterate over this collection starting from the tail and run the callback with passing the key and
335
- * item in the iteration loop.
336
- * @param in_callback - a function that we will call on each item
337
- * of this collection. If the callback returns false then the iteration will exit early.
338
- */
339
- iterateFromTail(in_callback) {
340
- let key; let continu;
341
- for (let i = this._order.length - 1; i >= 0; i--) {
342
- key = this._order[i];
343
- continu = in_callback(key, this._items[key]);
344
- if (continu === false) {
345
- break;
346
- }
347
- }
348
- }
349
-
350
- /**
351
- * @returns Return an object containing the items of this collection
352
- */
353
- getItems(): { [key: string]: T; } {
354
- const result = {};
355
-
356
- _.each(this._items, function(item, key) {
357
- result[key] = item;
358
- });
359
-
360
- return result;
361
- }
362
-
363
- /**
364
- * Return the list of keys
365
- * @returns List of keys
366
- */
367
- getKeys(): string[] {
368
- return Object.keys(this._items);
369
- }
370
-
371
- /**
372
- * Method used to get the first element in the collection along with its key.
373
- */
374
- peak() {
375
- return {
376
- item: this._items[this._order[0]],
377
- key: this._order[0],
378
- };
379
- }
380
-
381
- /**
382
- * Clear this collection
383
- * @returns this collection
384
- */
385
- clear() {
386
- if (_.isEmpty(this._items)) {
387
- return this;
388
- }
389
-
390
- this.onClear(this._items);
391
-
392
- // Best to just iterate through and remove everything, so that OnRemove
393
- // handlers are called.
394
-
395
- _.each(this.getKeys(), (key) => {
396
- this.remove(key);
397
- });
398
-
399
- return this;
400
- }
401
-
402
- /**
403
- * Copy the items of in_collection to this collection.
404
- * @param in_collection - the collection we want to
405
- * copy from.
406
- * @returns new Collection
407
- */
408
- clone(): Collection<T> {
409
- const newCol = new Collection<T>(this._name, this._type);
410
- newCol.bulkAdd(this._items);
411
- return newCol;
412
- }
413
-
414
- /**
415
- * Copy the items of in_collection to this collection.
416
- * @param in_collection - the collection we want to
417
- * copy from.
418
- */
419
- copy(in_collection: Collection<T>) {
420
- this.clear();
421
- const its = in_collection.items;
422
-
423
- _.each(its, (item, key) => {
424
- this.add(key, item);
425
- });
426
- }
427
-
428
- get values() {
429
- return this.getAsArray();
430
- }
21
+ protected _items: { [key: string]: T } = {};
22
+ protected _order: (string | number)[] = [];
23
+
24
+ /**
25
+ * @param _name - a friendly name to describe this collection. If undefined
26
+ * the collection will have a default "Untitled Collection" assigned to its name.
27
+ * @param _type - optional parameter pointing to the constructor
28
+ * of a type this Collection will host.
29
+ */
30
+ constructor(protected _name = "Untitled Collection", protected _type?: new () => any) {}
31
+
32
+ // Pass-thru binding handles
33
+ onAdd(in_key, in_value) {}
34
+ onRemove(in_key, in_value) {}
35
+ onClear(in_items) {}
36
+
37
+ public get items() {
38
+ return this._items;
39
+ }
40
+
41
+ public get keys() {
42
+ return Object.keys(this._items);
43
+ }
44
+
45
+ /**
46
+ * @param in_key - Key to store the value under
47
+ * @param in_value - Value to store in the collection
48
+ * @returns Return the value passed in
49
+ */
50
+ add(in_key: number | string, in_value: T): T {
51
+ this._checkType(in_value);
52
+ this._checkIsNewKey(in_key);
53
+
54
+ this._items[in_key] = in_value;
55
+ this._order.push(in_key);
56
+
57
+ this.onAdd(in_key, in_value);
58
+
59
+ return in_value;
60
+ }
61
+
62
+ /**
63
+ * Checks if in_value's type is equal to this Collection type. If this collection
64
+ * has no type set, the check will pass.
65
+ *
66
+ * @param in_value - A value that is equal to the type managed by this collection.
67
+ * @returns Return true if the type is a valid type for this
68
+ * collection, throw otherwise.
69
+ */
70
+ private _checkType(in_value: T) {
71
+ if (this._type && !(in_value instanceof this._type)) {
72
+ throw new Error(MSGS.TYPE_MISMATCH);
73
+ } else {
74
+ return true;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Bulk add items.
80
+ * @param in_items - List of key-value pairs to be stored in the collection
81
+ * @returns this collection after add
82
+ */
83
+ bulkAdd(in_items: { [key: string]: T }) {
84
+ _.each(in_items, (item, key) => {
85
+ this.add(key, item);
86
+ });
87
+ return this;
88
+ }
89
+
90
+ /**
91
+ * Bulk remove items.
92
+ * @param in_items - List of key-value items to be removed
93
+ * @returns this collection after add
94
+ */
95
+ bulkRemove(in_items: { [key: string]: T }) {
96
+ _.each(in_items, (item, key) => {
97
+ this.remove(key);
98
+ });
99
+
100
+ return this;
101
+ }
102
+
103
+ /**
104
+ * Test if this collection is empty
105
+ * @returns true if empty, false otherwise
106
+ * */
107
+ isEmpty() {
108
+ return _.isEmpty(this._items);
109
+ }
110
+
111
+ /**
112
+ * Return the first item in the collection, null if empty
113
+ * @returns first item, or undefined if empty
114
+ * */
115
+ getFirstItem(): T | undefined {
116
+ const index = _.first(this._order);
117
+ return index === undefined ? index : this._items[index];
118
+ }
119
+
120
+ /**
121
+ * Return the last item in the collection, null if empty
122
+ * @returns - last item, or undefined if empty
123
+ * */
124
+ getLastItem(): T | undefined {
125
+ const index = _.last(this._order);
126
+ return index === undefined ? index : this._items[index];
127
+ }
128
+
129
+ /**
130
+ * @returns Returns the type of collection (Array, etc.)
131
+ */
132
+ getType() {
133
+ return this._type;
134
+ }
135
+
136
+ /**
137
+ * @returns Returns the name of the collection
138
+ */
139
+ getName(): string {
140
+ return this._name;
141
+ }
142
+
143
+ /**
144
+ * Filter out by function
145
+ * @param in_filterFunction - with arguments key and item
146
+ * @returns A new filtered collection
147
+ */
148
+ filter(in_filterFunction: (key: string, item: T) => boolean): Collection<T> {
149
+ const rtn = new Collection<T>();
150
+
151
+ const filterCb = function (in_key, in_item) {
152
+ const keeper = in_filterFunction(in_key, in_item);
153
+ if (keeper) {
154
+ rtn.add(in_key, in_item);
155
+ }
156
+ };
157
+
158
+ this.iterate(filterCb);
159
+
160
+ return rtn;
161
+ }
162
+
163
+ /**
164
+ * Filter out all keys NOT matching the in_filterKey
165
+ * @param in_filterKey - a single key or an array of keys, if the
166
+ * item matches any of the keys it will be filtered in.
167
+ * @returns New filtered collection with all the items
168
+ * matching at least one key.
169
+ */
170
+ filterByKey(in_filterKey: string | string[]): Collection<T> {
171
+ const rtn = new Collection<T>();
172
+
173
+ const filterCb = _.isArray(in_filterKey)
174
+ ? function (in_key, in_item) {
175
+ if (in_filterKey.includes(in_key)) {
176
+ rtn.add(in_key, in_item);
177
+ }
178
+ }
179
+ : function (in_key, in_item) {
180
+ if (in_key === in_filterKey) {
181
+ rtn.add(in_key, in_item);
182
+ }
183
+ };
184
+
185
+ this.iterate(filterCb);
186
+
187
+ return rtn;
188
+ }
189
+
190
+ /**
191
+ * Filter out all keys NOT matching the in_filterValue
192
+ * @param in_filterValue - Value to filter on
193
+ * @returns Return a filtered collection
194
+ */
195
+ filterByValue(in_filterValue: T): Collection<T> {
196
+ const rtn = new Collection<T>();
197
+
198
+ const filterCb = function (in_key, in_item) {
199
+ if (in_item === in_filterValue) {
200
+ rtn.add(in_key, in_item);
201
+ }
202
+ };
203
+
204
+ this.iterate(filterCb);
205
+
206
+ return rtn;
207
+ }
208
+
209
+ /**
210
+ * Remove an item from this Collection. This method returns a Boolean indicating
211
+ * the success or failure of the removal. This is practical because if we were
212
+ * to throw an error when the key doesn't exist, it would require additional
213
+ * checks by the caller to make sure this key exists prior to removal. Which
214
+ * would make the attempt of removal more verbose and also costly because the
215
+ * caller would have to keep a list – somewhere else – of the things he can
216
+ * and cannot remove.
217
+ *
218
+ * @param in_key - the key we wish to remove
219
+ * @returns true if the key exists and was removed, false otherwise.
220
+ */
221
+ remove(in_key: number | string): boolean {
222
+ if (!this.has(in_key)) {
223
+ return false;
224
+ }
225
+
226
+ const remember = this._items[in_key];
227
+
228
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
229
+ delete this._items[in_key];
230
+ this._order.splice(this._order.indexOf(in_key), 1);
231
+
232
+ this.onRemove(in_key, remember);
233
+
234
+ return true;
235
+ }
236
+
237
+ /**
238
+ * Return the number of items in this Collection
239
+ * @returns the number of items in the collection
240
+ */
241
+ getCount(): number {
242
+ return this._order.length;
243
+ }
244
+
245
+ /**
246
+ * Returns this collection as an ordered Array
247
+ * @returns Array including the values
248
+ */
249
+ getAsArray() {
250
+ const rtnArr: T[] = new Array(this.getCount());
251
+
252
+ for (let i = 0; i < this._order.length; i++) {
253
+ rtnArr[i] = this._items[this._order[i]];
254
+ }
255
+
256
+ return rtnArr;
257
+ }
258
+
259
+ /**
260
+ * @param in_key - the key we are looking for
261
+ * @returns true if the item exists
262
+ */
263
+ has(in_key: string | number): boolean {
264
+ return Object.prototype.hasOwnProperty.call(this._items, in_key);
265
+ }
266
+
267
+ /**
268
+ * Return an item associated with the given key
269
+ * @param in_key - the key for the item in this
270
+ * Collection
271
+ * @returns The item
272
+ */
273
+ item(in_key: number | string) {
274
+ return this._items[in_key];
275
+ }
276
+
277
+ /**
278
+ * Checks if this is a new key in the collection. Throw if already exists.
279
+ * @param in_key - The key to check against
280
+ * @returns true if key is new
281
+ */
282
+ private _checkIsNewKey(in_key: number | string) {
283
+ if (this.has(in_key)) {
284
+ throw new Error(`${MSGS.KEY_ALREADY_EXISTS} ${in_key}`);
285
+ }
286
+
287
+ return true;
288
+ }
289
+
290
+ /**
291
+ * Checks if the key exists in the collection. Throw if not.
292
+ * @param in_key - the key to check against
293
+ * @returns true if key exists
294
+ */
295
+ private _checkKeyExists(in_key: number | string) {
296
+ if (!this.has(in_key)) {
297
+ throw new Error(`${MSGS.KEY_DOES_NOT_EXIST} ${in_key}`);
298
+ }
299
+
300
+ return true;
301
+ }
302
+
303
+ /**
304
+ * Set an existing key to a value. If key doesn't exist this call will throw
305
+ * an error.
306
+ * @param in_key - The key we want to modify
307
+ * @param in_value - The value we are to set at this key
308
+ * @returns The value passed in
309
+ */
310
+ set(in_key: string, in_value: T) {
311
+ this._checkKeyExists(in_key);
312
+
313
+ this._items[in_key] = in_value;
314
+
315
+ return in_value;
316
+ }
317
+
318
+ /**
319
+ * Iterate over this collection and run the callback with passing the key and
320
+ * item in the iteration loop.
321
+ * @param in_callback - A function that we will call on each item
322
+ * of this collection. If the callback returns false then the iteration will exit early.
323
+ */
324
+ iterate(in_callback) {
325
+ for (const key of this._order) {
326
+ const continu = in_callback(key, this._items[key]);
327
+ if (continu === false) {
328
+ break;
329
+ }
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Iterate over this collection starting from the tail and run the callback with passing the key and
335
+ * item in the iteration loop.
336
+ * @param in_callback - a function that we will call on each item
337
+ * of this collection. If the callback returns false then the iteration will exit early.
338
+ */
339
+ iterateFromTail(in_callback) {
340
+ let key;
341
+ let continu;
342
+ for (let i = this._order.length - 1; i >= 0; i--) {
343
+ key = this._order[i];
344
+ continu = in_callback(key, this._items[key]);
345
+ if (continu === false) {
346
+ break;
347
+ }
348
+ }
349
+ }
350
+
351
+ /**
352
+ * @returns Return an object containing the items of this collection
353
+ */
354
+ getItems(): { [key: string]: T } {
355
+ const result = {};
356
+
357
+ _.each(this._items, function (item, key) {
358
+ result[key] = item;
359
+ });
360
+
361
+ return result;
362
+ }
363
+
364
+ /**
365
+ * Return the list of keys
366
+ * @returns List of keys
367
+ */
368
+ getKeys(): string[] {
369
+ return Object.keys(this._items);
370
+ }
371
+
372
+ /**
373
+ * Method used to get the first element in the collection along with its key.
374
+ */
375
+ peak() {
376
+ return {
377
+ item: this._items[this._order[0]],
378
+ key: this._order[0],
379
+ };
380
+ }
381
+
382
+ /**
383
+ * Clear this collection
384
+ * @returns this collection
385
+ */
386
+ clear() {
387
+ if (_.isEmpty(this._items)) {
388
+ return this;
389
+ }
390
+
391
+ this.onClear(this._items);
392
+
393
+ // Best to just iterate through and remove everything, so that OnRemove
394
+ // handlers are called.
395
+
396
+ _.each(this.getKeys(), (key) => {
397
+ this.remove(key);
398
+ });
399
+
400
+ return this;
401
+ }
402
+
403
+ /**
404
+ * Copy the items of in_collection to this collection.
405
+ * @param in_collection - the collection we want to
406
+ * copy from.
407
+ * @returns new Collection
408
+ */
409
+ clone(): Collection<T> {
410
+ const newCol = new Collection<T>(this._name, this._type);
411
+ newCol.bulkAdd(this._items);
412
+ return newCol;
413
+ }
414
+
415
+ /**
416
+ * Copy the items of in_collection to this collection.
417
+ * @param in_collection - the collection we want to
418
+ * copy from.
419
+ */
420
+ copy(in_collection: Collection<T>) {
421
+ this.clear();
422
+ const its = in_collection.items;
423
+
424
+ _.each(its, (item, key) => {
425
+ this.add(key, item);
426
+ });
427
+ }
428
+
429
+ get values() {
430
+ return this.getAsArray();
431
+ }
431
432
  }