@luminix/support 0.0.1-beta.0 → 0.0.1-beta.2

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/src/Collection.ts DELETED
@@ -1,1398 +0,0 @@
1
- import EventSource, { Event } from "./Contracts/EventSource";
2
- import * as Arr from './Arr';
3
- import * as Obj from './Obj';
4
- import { Constructor, TypeOf } from "./Js";
5
- import { Operator } from "./Query";
6
-
7
- const emitChange = (collection: Collection<any>) => {
8
- collection.emit('change', {
9
- items: collection.all(),
10
- source: collection,
11
- });
12
- };
13
-
14
- const get = <T>(arrayOrCollection: T[] | Collection<T>, index: number): T | null => {
15
- if (Array.isArray(arrayOrCollection)) {
16
- return arrayOrCollection[index] ?? null;
17
- }
18
-
19
- return arrayOrCollection.get(index);
20
- };
21
-
22
- export type CollectionChanged<T> = { items: T[] };
23
-
24
-
25
- export type CollectionEvents<T> = {
26
- 'change': (e: Event<CollectionChanged<T>, Collection<T>>) => void;
27
- };
28
-
29
- export type CollectionIteratorCallback<T = unknown, R = void> = (value: T, index: number, collection: Collection<T>) => R;
30
-
31
- export type CollectionPipeCallback<T = unknown, R = unknown> = (collection: Collection<T>) => R;
32
-
33
- export type CollectionSortCallback<T = unknown> = (a: T, b: T) => number;
34
-
35
-
36
- export function isCollection(instance: unknown) {
37
- return instance instanceof Collection;
38
- }
39
-
40
-
41
- export default class Collection<T> extends EventSource<CollectionEvents<T>> {
42
-
43
- #items: T[];
44
-
45
- constructor(
46
- items: Array<T> = []
47
- ) {
48
- super();
49
- this.#items = items;
50
- }
51
-
52
- get items() {
53
- return [...this.#items];
54
- }
55
-
56
- [Symbol.iterator]() {
57
- return this.#items[Symbol.iterator]();
58
- }
59
-
60
- [Symbol.toStringTag] = 'Collection';
61
-
62
- all(): T[] {
63
- return [...this.#items];
64
- }
65
-
66
- average(): number
67
- average<K extends keyof T>(key: K): number;
68
- average(key?: keyof T): number {
69
- if (typeof key === 'string') {
70
- return this.avg(key);
71
- }
72
- return this.avg();
73
- }
74
-
75
- avg(): number
76
- avg<K extends keyof T>(key: K): number;
77
- avg(key?: keyof T): number {
78
-
79
- if (typeof key === 'string') {
80
- return this.sum(key) / this.#items.length;
81
- }
82
-
83
- return this.sum() / this.#items.length;
84
- }
85
-
86
- chunk(size: number): Collection<Collection<T>> {
87
- const chunks = [];
88
- for (let i = 0; i < this.#items.length; i += size) {
89
- chunks.push(this.#items.slice(i, i + size));
90
- }
91
-
92
- return new Collection(chunks.map(chunk => new Collection(chunk)));
93
- }
94
-
95
- chunkWhile(callback: CollectionIteratorCallback<T, boolean>): Collection<Collection<T>> {
96
- const chunks = [];
97
- let nextChunk = new Collection<T>();
98
- for (let i = 0; i < this.#items.length; i++) {
99
- if (callback(this.#items[i], i, nextChunk)) {
100
- nextChunk.push(this.#items[i]);
101
- } else {
102
- chunks.push(nextChunk);
103
- nextChunk = new Collection<T>();
104
- }
105
- }
106
-
107
- if (nextChunk.count() > 0) {
108
- chunks.push(nextChunk);
109
- }
110
-
111
- return new Collection(chunks);
112
- }
113
-
114
- collapse(): Collection<unknown> {
115
- return new Collection<unknown>(this.#items.flat());
116
- }
117
-
118
-
119
- collect(): Collection<T> {
120
- return new Collection(this.#items);
121
- }
122
-
123
- combine(values: Collection<any> | any[]): Record<string, any> {
124
- const combined: Record<string, any> = {};
125
-
126
- this.#items.forEach((key, index) => {
127
- if ('string' !== typeof key) {
128
- throw new TypeError('The `combine` method expects the keys to be strings');
129
- }
130
-
131
- combined[key] = get(values, index);
132
- });
133
-
134
- return combined;
135
- }
136
-
137
-
138
- concat(collection: Collection<unknown> | unknown[]): Collection<unknown> {
139
- if (!Array.isArray(collection)) {
140
- return new Collection([...this.#items, ...collection.all()]);
141
- }
142
- return new Collection([...this.#items, ...collection]);
143
- }
144
-
145
- contains(value: T): boolean;
146
- contains(key: keyof T, value: T): boolean;
147
- contains(callback: CollectionIteratorCallback<T, boolean>): boolean;
148
- contains(valueOrKeyOrCallback: T | keyof T | CollectionIteratorCallback<T, boolean>, value?: T): boolean {
149
- if (typeof valueOrKeyOrCallback === 'function') {
150
- return this.#items.some((item, index) => {
151
- return (valueOrKeyOrCallback as CollectionIteratorCallback<T, boolean>)(item, index, this);
152
- });
153
- }
154
-
155
- return this.#items.some((item) => {
156
- if (typeof value === 'undefined') {
157
- return item == valueOrKeyOrCallback;
158
- }
159
- if (typeof valueOrKeyOrCallback !== 'string') {
160
- throw new TypeError('The key must be a string');
161
- }
162
-
163
- return item[valueOrKeyOrCallback as keyof T] == value;
164
- });
165
- }
166
-
167
- containsOneItem(): boolean {
168
- return this.#items.length === 1;
169
- }
170
-
171
- containsStrict(value: T): boolean;
172
- containsStrict(key: keyof T, value: T): boolean;
173
- containsStrict(callback: CollectionIteratorCallback<T, boolean>): boolean;
174
- containsStrict(valueOrKeyOrCallback: T | keyof T | (CollectionIteratorCallback<T, boolean>), value?: T): boolean {
175
- if (typeof valueOrKeyOrCallback === 'function') {
176
- return this.#items.some((item, index) => {
177
- return (valueOrKeyOrCallback as CollectionIteratorCallback<T, boolean>)(item, index, this);
178
- });
179
- }
180
-
181
- return this.#items.some((item) => {
182
- if (typeof value === 'undefined') {
183
- return item === valueOrKeyOrCallback;
184
- }
185
- if (typeof valueOrKeyOrCallback !== 'string') {
186
- throw new TypeError('The key must be a string');
187
- }
188
-
189
- return item[valueOrKeyOrCallback as keyof T] === value;
190
- });
191
- }
192
-
193
- count(): number {
194
- return this.#items.length;
195
- }
196
-
197
- countBy(callback?: CollectionIteratorCallback<T, string | number>): Record<string | number, number> {
198
- if (typeof callback === 'function') {
199
- return this.#items.reduce((carry, item, index) => {
200
- const key = callback(item, index, this);
201
- carry[key] = carry[key] ? carry[key] + 1 : 1;
202
- return carry;
203
- }, {} as Record<string, number>);
204
- }
205
-
206
- return this.#items.reduce((carry, item) => {
207
- if (!['string', 'number'].includes(typeof item)) {
208
- throw new TypeError('The countBy method expects the items to be strings or numbers');
209
- }
210
-
211
- carry[String(item)] = carry[String(item)] ? carry[String(item)] + 1 : 1;
212
- return carry;
213
- }, {} as Record<string, number>);
214
- }
215
-
216
- crossJoin<V>(...collections: (Collection<V> | V[])[]): Collection<Array<V | T>> {
217
- return new Collection(Arr.cartesian<V|T>(
218
- this.#items,
219
- ...collections.map((collection) => {
220
- if (!Array.isArray(collection)) {
221
- return collection.all();
222
- }
223
- return collection;
224
- })
225
- ));
226
- }
227
-
228
-
229
- diff(collection: Collection<T> | T[]): Collection<T> {
230
- if (!Array.isArray(collection)) {
231
- return new Collection(this.#items.filter(item => !collection.contains(item)));
232
- }
233
- return new Collection(this.#items.filter(item => !collection.includes(item)));
234
- }
235
-
236
- doesntContain(value: T): boolean;
237
- doesntContain(key: keyof T, value: T): boolean;
238
- doesntContain(callback: CollectionIteratorCallback<T, boolean>): boolean;
239
- doesntContain(valueOrKeyOrCallback: T | keyof T | (CollectionIteratorCallback<T, boolean>), value?: T): boolean {
240
- if (typeof valueOrKeyOrCallback === 'function') {
241
- // return !this.items.some(valueOrKeyOrCallback as IteratorCallback<T, boolean>);
242
- return this.#items.every((item, index) => {
243
- return !(valueOrKeyOrCallback as CollectionIteratorCallback<T, boolean>)(item, index, this);
244
- });
245
- }
246
-
247
- return this.#items.every((item) => {
248
- if (typeof value === 'undefined') {
249
- return item != valueOrKeyOrCallback;
250
- }
251
-
252
- if (typeof valueOrKeyOrCallback !== 'string') {
253
- throw new TypeError('The key must be a string');
254
- }
255
-
256
- return item[valueOrKeyOrCallback as keyof T] != value;
257
- });
258
- }
259
-
260
- dump(): void {
261
- console.log(this.toArray());
262
- }
263
-
264
- duplicates(): Collection<T>;
265
- duplicates<K extends keyof T>(key: K): Collection<T[K]>;
266
- duplicates<K extends keyof T>(key?: keyof K): Collection<T> | Collection<T[K]> {
267
- if (typeof key === 'string') {
268
- return new Collection(this.#items.reduce((carry, item, index) => {
269
- if (this.#items.slice(index + 1).some((next) => next[key as keyof T] == item[key as keyof T])) {
270
- carry.push(item[key as K]);
271
- }
272
- return carry;
273
- }, [] as (T[K])[]));
274
- }
275
-
276
- return new Collection(this.#items.reduce((carry, item, index) => {
277
- if (this.#items.slice(index + 1).some((next) => next == item)) {
278
- carry.push(item);
279
- }
280
- return carry;
281
- }, [] as T[]));
282
- }
283
-
284
-
285
- duplicatesStrict(): Collection<T>;
286
- duplicatesStrict<K extends keyof T>(key: K): Collection<T[K]>;
287
- duplicatesStrict<K extends keyof T>(key?: keyof K): Collection<T> | Collection<T[K]> {
288
- if (typeof key === 'string') {
289
- return new Collection(this.#items.reduce((carry, item, index) => {
290
- if (this.#items.slice(index + 1).some((next) => next[key as keyof T] === item[key as keyof T])) {
291
- carry.push(item[key as K]);
292
- }
293
- return carry;
294
- }, [] as (T[K])[]));
295
- }
296
-
297
- return new Collection(this.#items.reduce((carry, item, index) => {
298
- if (this.#items.slice(index + 1).some((next) => next === item)) {
299
- carry.push(item);
300
- }
301
- return carry;
302
- }, [] as T[]));
303
- }
304
-
305
- each(callback: CollectionIteratorCallback<T, void | false>): this {
306
- let index = 0;
307
-
308
- for (const item of this) {
309
- if (false === callback(item, index, this)) {
310
- break;
311
- }
312
- index++;
313
- }
314
-
315
- return this;
316
- }
317
-
318
- eachSpread(callback: (...args: unknown[]) => void | false): this {
319
-
320
- for (const item of this) {
321
- if (!Array.isArray(item) && !isCollection(item)) {
322
- throw new TypeError('The items in the collection must be arrays or collections');
323
- }
324
-
325
- const args = Array.isArray(item)
326
- ? item
327
- : item.all();
328
-
329
- if (false === callback(...args)) {
330
- break;
331
- }
332
- }
333
-
334
- return this;
335
- }
336
-
337
- ensure(type: TypeOf | Constructor | (TypeOf | Constructor)[]): this {
338
- const types = Array.isArray(type) ? type : [type];
339
-
340
- this.#items.forEach((item, index) => {
341
- if (!types.some((type) => {
342
- if ('string' === typeof type) {
343
- return typeof item === type;
344
- }
345
- return item instanceof type;
346
- })) {
347
- throw new TypeError(`The item at index ${index} is not of the expected type`);
348
- }
349
- });
350
-
351
- return this;
352
- }
353
-
354
- every(callback: CollectionIteratorCallback<T, boolean>): boolean {
355
- return this.#items.every((item, index) => {
356
- return callback(item, index, this);
357
- });
358
- }
359
-
360
- except(indexes: Array<number>): Collection<T> {
361
- return new Collection(this.#items.filter((_, index) => !indexes.includes(index)));
362
- }
363
-
364
-
365
- filter(callback?: CollectionIteratorCallback<T, boolean>): Collection<T> {
366
- return new Collection(this.#items.filter((item, index) => {
367
- if (typeof callback !== 'function') {
368
- return !!item;
369
- }
370
- return callback(item, index, this);
371
- }));
372
- }
373
-
374
-
375
- first(callback?: CollectionIteratorCallback<T, boolean>): T | null {
376
- if (typeof callback === 'function') {
377
- return this.#items.find((item, index) => {
378
- return callback(item, index, this);
379
- }) ?? null;
380
- }
381
- return this.#items[0] ?? null;
382
-
383
- }
384
-
385
- firstOrFail(callback?: CollectionIteratorCallback<T, boolean>): T {
386
- const item = this.first(callback);
387
- if (item === null) {
388
- throw new Error('No matching item found');
389
- }
390
- return item;
391
- }
392
-
393
- firstWhere<K extends keyof T>(key: K): T | null;
394
- firstWhere<K extends keyof T>(key: K, value: T[K]): T | null;
395
- firstWhere<K extends keyof T>(key: K, operator: Operator, value: T[K]): T | null
396
- firstWhere<K extends keyof T>(key: K, operator?: Operator | T[K], value?: T[K]): T | null {
397
- if (typeof key !== 'string') {
398
- throw new TypeError('The key must be a string');
399
- }
400
-
401
- if (typeof operator === 'undefined') {
402
- return this.first(item => !!item[key as keyof T]);
403
- }
404
-
405
- if (typeof value === 'undefined') {
406
- return this.#items.find(item => item[key as keyof T] == operator) ?? null;
407
- }
408
-
409
- if (typeof operator !== 'string') {
410
- throw new TypeError('The operator must be a string');
411
- }
412
-
413
- if (value === null) {
414
- return this.#items.find(item => item[key as keyof T] === null) ?? null;
415
- }
416
-
417
- return this.#items.find(item => {
418
- switch (operator) {
419
- case '=':
420
- return item[key as keyof T] == value;
421
- case '!=':
422
- return item[key as keyof T] != value;
423
- case '>':
424
- return item[key as keyof T] > value;
425
- case '<':
426
- return item[key as keyof T] < value;
427
- case '>=':
428
- return item[key as keyof T] >= value;
429
- case '<=':
430
- return item[key as keyof T] <= value;
431
- default:
432
- throw new Error('Unsupported operator');
433
- }
434
- }) ?? null;
435
-
436
- }
437
-
438
- flatMap<R>(callback: CollectionIteratorCallback<T, R | R[]>): Collection<R> {
439
- return new Collection(this.#items.flatMap((item, index) => {
440
- return callback(item, index, this);
441
- }));
442
- }
443
-
444
- forget(key: number): this {
445
- this.#items.splice(key, 1);
446
- emitChange(this);
447
- return this;
448
- }
449
-
450
- forPage(page: number, perPage: number): Collection<T> {
451
- return new Collection(this.#items.slice((page - 1) * perPage, page * perPage));
452
- }
453
-
454
- get(key: number): T | null;
455
- get<R>(key: number, defaultValue: R): T | R;
456
- get<R>(key: number, defaultValue: () => R): T | R;
457
- get(key: unknown, defaultValue?: unknown): unknown {
458
- if (typeof key !== 'number') {
459
- throw new TypeError('The key must be a number');
460
- }
461
-
462
- if (typeof defaultValue === 'undefined') {
463
- return this.#items[key] ?? null;
464
- }
465
-
466
- if (typeof defaultValue === 'function') {
467
- return this.#items[key] ?? defaultValue();
468
- }
469
-
470
- return this.#items[key] ?? defaultValue;
471
- }
472
-
473
- groupBy(key: keyof T): Record<string, T[]>;
474
- groupBy(callback: CollectionIteratorCallback<T, string | string[]>): Record<string, T[]>;
475
- groupBy(keys: (keyof T | CollectionIteratorCallback<T, string | string[]>)[]): Record<string, unknown>;
476
- groupBy(arg: keyof T | CollectionIteratorCallback<T, string | string[]> | (keyof T | CollectionIteratorCallback<T, string | string[]>)[]): Record<string, unknown> {
477
-
478
- const stack = Array.isArray(arg) ? arg : [arg];
479
-
480
- return this.#items.reduce((carry, item, index) => {
481
- const segments = stack.map((key) => {
482
- if (typeof key === 'function') {
483
- const result = key(item, index, this);
484
- return Array.isArray(result)
485
- ? result
486
- : [result];
487
- }
488
- return [String(item[key])];
489
- });
490
-
491
- const paths = Arr.cartesian(...segments);
492
-
493
- paths.forEach((path) => {
494
- const key = Array.isArray(path) ? path.join('.') : path;
495
-
496
- Obj.set(carry, key, [
497
- ...(Obj.get(carry, key, []) as T[]),
498
- item,
499
- ]);
500
- });
501
-
502
- return carry;
503
- }, {} as Record<string, unknown>);
504
-
505
- }
506
-
507
-
508
- has(index: number): boolean {
509
- return this.#items.length > index;
510
- }
511
-
512
- hasAny(indexes: number[]): boolean {
513
- return indexes.some((index) => this.has(index));
514
- }
515
-
516
- implode(glue: string): string;
517
- implode(key: keyof T, glue: string): string;
518
- implode(callback: CollectionIteratorCallback<T, string>, glue: string): string;
519
- implode(keyOrGlueOrCallback: string | keyof T | CollectionIteratorCallback<T, string>, glue?: string): string {
520
- if (typeof glue === 'undefined') {
521
- if (typeof keyOrGlueOrCallback !== 'string') {
522
- throw new TypeError('The glue must be a string');
523
- }
524
-
525
- if (!this.#items.every((item) => ['string', 'number'].includes(typeof item))) {
526
- throw new TypeError('The items must be strings or numbers');
527
- }
528
-
529
- return this.#items.join(keyOrGlueOrCallback);
530
- }
531
-
532
- if (typeof keyOrGlueOrCallback === 'function') {
533
- return this.#items.map((item, index) => {
534
- return keyOrGlueOrCallback(item, index, this);
535
- }).join(glue);
536
- }
537
-
538
- if (typeof keyOrGlueOrCallback !== 'string') {
539
- throw new TypeError('The key must be a string');
540
- }
541
-
542
- if (!this.#items.every((item) => typeof item === 'object')) {
543
- throw new TypeError('The items must be objects');
544
- }
545
-
546
- return this.#items.map((item) => {
547
- return item![keyOrGlueOrCallback as keyof T];
548
- }).join(glue);
549
-
550
- }
551
-
552
- intersect(values: Collection<T> | T[]): Collection<T> {
553
- if (!Array.isArray(values)) {
554
- return new Collection(this.#items.filter(item => values.contains(item)));
555
- }
556
- return new Collection(this.#items.filter(item => values.includes(item)));
557
- }
558
-
559
- isEmpty(): boolean {
560
- return this.#items.length === 0;
561
- }
562
-
563
- isNotEmpty(): boolean {
564
- return !this.isEmpty();
565
- }
566
-
567
- join(glue: string): string;
568
- join(glue: string, final: string): string;
569
- join(glue: string, final?: string): string {
570
- if (typeof final === 'undefined') {
571
- return this.#items.join(glue);
572
- }
573
-
574
- return this.#items.slice(0, -1).join(glue) + final + this.#items[this.#items.length - 1];
575
- }
576
-
577
- keyBy(key: keyof T): Record<string, T>;
578
- keyBy(callback: CollectionIteratorCallback<T, string>): Record<string, T>;
579
- keyBy(keyOrCallback: unknown): Record<string, T> {
580
- if (typeof keyOrCallback === 'function') {
581
- return this.#items.reduce((carry, item, index) => {
582
- carry[keyOrCallback(item, index, this)] = item;
583
- return carry;
584
- }, {} as Record<string, T>);
585
- }
586
-
587
- if (typeof keyOrCallback !== 'string') {
588
- throw new TypeError('The key must be a string');
589
- }
590
-
591
- return this.#items.reduce((carry, item) => {
592
- carry[String(item[keyOrCallback as keyof T])] = item;
593
- return carry;
594
- }, {} as Record<string, T>);
595
-
596
- }
597
-
598
- last(callback?: CollectionIteratorCallback<T, boolean> | undefined): T | null {
599
- if (typeof callback === 'function') {
600
- return this.#items.toReversed().find((item, index) => {
601
- return callback(item, index, this);
602
- }) ?? null;
603
- }
604
- return this.#items[this.#items.length - 1] ?? null;
605
- }
606
-
607
- map<R>(callback: CollectionIteratorCallback<T, R>): Collection<R> {
608
- return new Collection(this.#items.map((item, index) => callback(item, index, this)));
609
- }
610
-
611
- mapInto<R extends Constructor<InstanceType<R>>>(constructor: R): Collection<InstanceType<R>> {
612
- return new Collection(this.#items.map((item) => new constructor(item)));
613
- }
614
-
615
- mapSpread<R>(callback: (...args: unknown[]) => R): Collection<R> {
616
- return new Collection(this.#items.map((item) => {
617
- if (!Array.isArray(item) && !isCollection(item)) {
618
- throw new TypeError('The items in the collection must be arrays or collections');
619
- }
620
-
621
- const args = Array.isArray(item)
622
- ? item
623
- : item.all();
624
-
625
- return callback(...args);
626
- }));
627
-
628
- }
629
-
630
- mapToGroups<R>(callback: CollectionIteratorCallback<T, Record<string, R>>): Record<string, R[]> {
631
- return this.#items.reduce((carry, item, index) => {
632
- const groups = callback(item, index, this);
633
-
634
- Object.entries(groups).forEach(([key, value]) => {
635
- carry[key] = carry[key] ?? [];
636
- carry[key].push(value);
637
- });
638
-
639
- return carry;
640
- }, {} as Record<string, R[]>);
641
- }
642
-
643
- mapWithKeys<R>(callback: CollectionIteratorCallback<T, Record<string, R>>): Record<string, R> {
644
- return this.#items.reduce((carry, item, index) => {
645
- const keys = callback(item, index, this);
646
-
647
- Object.entries(keys).forEach(([key, value]) => {
648
- carry[key] = value;
649
- });
650
-
651
- return carry;
652
- }, {} as Record<string, R>);
653
- }
654
-
655
- max(): T | null;
656
- max<K extends keyof T>(key: K): T[K] | null;
657
- max<K extends keyof T>(key?: K): T | T[K] | null {
658
- if (typeof key === 'string') {
659
- return this.#items.reduce((carry, item) => {
660
- return item[key] > carry ? item[key] : carry;
661
- }, this.#items[0][key] as T[K]);
662
- }
663
-
664
- return this.#items.reduce((carry, item) => {
665
- return item > carry ? item : carry;
666
- }, this.#items[0] as T);
667
- }
668
-
669
- median(): T | null;
670
- median<K extends keyof T>(key: K): T[K] | null;
671
- median<K extends keyof T>(key?: K): T | T[K] | null {
672
- if (typeof key === 'string') {
673
- const sorted = this.pluck(key).sort();
674
- const middle = Math.floor(sorted.count() / 2);
675
-
676
- if (sorted.count() % 2 === 0) {
677
- // return (sorted[middle - 1] + sorted[middle]) / 2;
678
- return new Collection([get(sorted, middle - 1), get(sorted, middle)]).avg() as T[K];
679
- }
680
-
681
- return get(sorted, middle);
682
- }
683
-
684
- const sorted = this.#items.toSorted();
685
- const middle = Math.floor(sorted.length / 2);
686
-
687
- if (sorted.length % 2 === 0) {
688
- // return (sorted[middle - 1] + sorted[middle]) / 2;
689
- return new Collection([sorted[middle - 1], sorted[middle]]).avg() as T;
690
- }
691
-
692
- return sorted[middle] ?? null;
693
-
694
- }
695
-
696
- merge(values: Collection<T> | T[]): Collection<T>;
697
- merge<R>(values: Collection<R> | R[]): Collection<T | R>;
698
- merge<R>(values: Collection<R> | R[]): Collection<T | R> {
699
- if (!Array.isArray(values)) {
700
- return new Collection([...this.#items, ...values.all()]);
701
- }
702
- return new Collection([...this.#items, ...values]);
703
- }
704
-
705
- min(): T | null;
706
- min<K extends keyof T>(key: K): T[K] | null;
707
- min<K extends keyof T>(key?: K): T | T[K] | null {
708
- if (typeof key === 'string') {
709
- return this.#items.reduce((carry, item) => {
710
- return item[key] < carry ? item[key] : carry;
711
- }, this.#items[0][key] as T[K]);
712
- }
713
-
714
- return this.#items.reduce((carry, item) => {
715
- return item < carry ? item : carry;
716
- }, this.#items[0] as T);
717
- }
718
-
719
- mode(): T[];
720
- mode<K extends keyof T>(key: K): T[K][];
721
- mode<K extends keyof T>(key?: K): T[] | T[K][] {
722
-
723
- const counts = typeof key === 'string'
724
- ? this.filter((item) => ['number', 'string'].includes(typeof item[key]))
725
- .countBy((item) => item[key] as string | number)
726
- : this.countBy();
727
-
728
- const max = Math.max(...Object.values(counts));
729
-
730
- return Object.entries(counts)
731
- .filter(([, count]) => count === max)
732
- .map(([value]) => value) as T[K][];
733
-
734
- }
735
-
736
- nth(n: number, offset: number = 0): Collection<T> {
737
- return this.chunk(n)//.get(offset) ?? collect();
738
- .filter((chunk) => chunk.count() > offset)
739
- .map((chunk) => chunk.get(offset) as T);
740
- }
741
-
742
- only(indexes: Array<number>): Collection<T> {
743
- return new Collection(this.#items.filter((_, index) => indexes.includes(index)));
744
- }
745
-
746
- pad<R>(size: number, value: R | null = null): Collection<T | R | null> {
747
- const result: (T|R|null)[] = this.#items.slice();
748
-
749
- while (result.length < Math.abs(size)) {
750
- if (size > 0) {
751
- result.push(value);
752
- } else {
753
- result.unshift(value);
754
- }
755
- }
756
-
757
- return new Collection(result);
758
- }
759
-
760
- partition(callback: CollectionIteratorCallback<T, boolean>): [Collection<T>, Collection<T>] {
761
- return [
762
- this.filter(callback),
763
- this.reject(callback),
764
- ];
765
- }
766
-
767
- percentage(callback: CollectionIteratorCallback<T, boolean>, precision = 2): number {
768
- return Math.round(
769
- 100 * (10 ^ precision) * this.filter(callback).count()
770
- / this.#items.length
771
- ) / (10 ^ precision);
772
- }
773
-
774
- pipe<R>(callback: CollectionPipeCallback<T, R>): R {
775
- return callback(this);
776
- }
777
-
778
- pipeInto<R extends Constructor<InstanceType<R>>>(constructor: R): InstanceType<R> {
779
- return new constructor(this);
780
- }
781
-
782
- pipeThrough<R>(pipeline: CollectionPipeCallback<unknown, Collection<unknown> | R>[]): R {
783
- // return pipeline.reduce((carry, callback) => callback(carry), this.collect());
784
-
785
- return pipeline.reduce((carry, callback) => {
786
-
787
- if (!isCollection(carry) && !Array.isArray(carry)) {
788
- throw new TypeError('The pipeline expects the carry to be a collection or an array');
789
- }
790
-
791
- const result: Collection<unknown> | R = callback(
792
- isCollection(carry)
793
- ? carry
794
- : new Collection(carry) as Collection<unknown>
795
- );
796
-
797
- return result;
798
- }, this as unknown) as R;
799
- }
800
-
801
- pluck<K extends keyof T>(key: K): Collection<T[K]> {
802
- return this.map((item) => item[key]);
803
- }
804
-
805
- pop(): T | null;
806
- pop(amount: number): Collection<T>;
807
- pop(amount = 1): T | Collection<T> | null {
808
- const items = this.#items.splice(this.#items.length - amount, amount);
809
- emitChange(this);
810
- return amount === 1
811
- ? (items[0] ?? null)
812
- : new Collection(items);
813
- }
814
-
815
- prepend(value: T): number {
816
- const length = this.#items.unshift(value);
817
- emitChange(this);
818
-
819
- return length;
820
- }
821
-
822
- pull(index: number): T | null {
823
- const item = this.#items.splice(index, 1)[0] ?? null;
824
- emitChange(this);
825
- return item;
826
- }
827
-
828
- push(...items: T[]): number {
829
- const length = this.#items.push(...items);
830
- emitChange(this);
831
-
832
- return length;
833
- }
834
-
835
- put(index: number, value: T): this {
836
- this.#items.splice(index, 1, value);
837
- emitChange(this);
838
- return this;
839
- }
840
-
841
- random(): T | null;
842
- random(amount: number): Collection<T>;
843
- random(amount = 1): T | Collection<T> | null {
844
- if (this.#items.length < amount) {
845
- throw new Error('The collection has fewer items than the requested amount');
846
- }
847
-
848
- const result = new Collection(Arr.sampleSize(this.#items, amount));
849
- return amount === 1
850
- ? result.first()
851
- : result;
852
- }
853
-
854
- reduce<R>(callback: (carry: R | null, item: T, index: number, collection: this) => R, initialValue: R | null = null): R | null {
855
- return this.#items.reduce((carry, value, index) => {
856
- return callback(carry, value, index, this);
857
- }, initialValue);
858
- }
859
-
860
- reject(callback: CollectionIteratorCallback<T, boolean>): Collection<T> {
861
- return this.filter((item, index) => !callback(item, index, this));
862
- }
863
-
864
- replace(data: Record<number, T>): Collection<T> {
865
- const items = this.#items.slice();
866
-
867
- Object.entries(data).forEach(([index, value]) => {
868
- items[parseInt(index)] = value;
869
- });
870
-
871
- return new Collection(items);
872
- }
873
-
874
- reverse(): Collection<T> {
875
- return new Collection(this.#items.toReversed());
876
- }
877
-
878
- search(value: T): number | false;
879
- search(value: T, strict: boolean): number | false;
880
- search(callback: CollectionIteratorCallback<T, boolean>): number | false;
881
- search(valueOrCallback: T | CollectionIteratorCallback<T, boolean>, strict = false): number | false {
882
- if (typeof valueOrCallback !== 'function' || this.#items.every((item) => typeof item === 'function')) {
883
- const index = this.#items.findIndex((item) => strict ? item === valueOrCallback : item == valueOrCallback);
884
-
885
- return index === -1 ? false : index;
886
- }
887
- const index = this.#items.findIndex((item, index) => {
888
- return (valueOrCallback as CollectionIteratorCallback<T, boolean>)(item, index, this);
889
- });
890
-
891
- return index === -1 ? false : index;
892
-
893
- }
894
-
895
- select<K extends Array<keyof T>>(keys: K): Collection<Pick<T, K[number]>> {
896
- return this.map((item) => {
897
- return keys.reduce((carry, key) => {
898
- carry[key] = item[key];
899
- return carry;
900
- }, {} as Pick<T, K[number]>);
901
- });
902
- }
903
-
904
- shift(): T | null;
905
- shift(count: number): Collection<T>;
906
- shift(count = 1): T | Collection<T> | null {
907
- const items = this.#items.splice(0, count);
908
- emitChange(this);
909
-
910
- return count === 1
911
- ? (items[0] ?? null)
912
- : new Collection(items);
913
- }
914
-
915
-
916
- shuffle(): Collection<T> {
917
- return new Collection(Arr.shuffle(this.#items));
918
- }
919
-
920
- skip(amount: number): Collection<T> {
921
- return new Collection(this.#items.slice(amount));
922
- }
923
-
924
- skipUntil(callback: CollectionIteratorCallback<T, boolean>): Collection<T>;
925
- skipUntil(value: T): Collection<T>;
926
- skipUntil(callback: CollectionIteratorCallback<T, boolean> | T): Collection<T> {
927
-
928
- if (typeof callback === 'function') {
929
- return this.skip(this.#items.findIndex((item, index) => {
930
- return (callback as CollectionIteratorCallback<T, boolean>)(item, index, this);
931
- }));
932
- }
933
-
934
- return this.skip(this.#items.findIndex((item) => item == callback));
935
-
936
- }
937
-
938
- skipWhile(callback: CollectionIteratorCallback<T, boolean>): Collection<T>;
939
- skipWhile(value: T): Collection<T>;
940
- skipWhile(callback: CollectionIteratorCallback<T, boolean> | T): Collection<T> {
941
-
942
- if (typeof callback === 'function') {
943
- return this.skip(this.#items.findIndex((item, index) => {
944
- return !(callback as CollectionIteratorCallback<T, boolean>)(item, index, this);
945
- }));
946
- }
947
-
948
- return this.skip(this.#items.findIndex((item) => item != callback));
949
-
950
- }
951
-
952
- slice(start?: number, size?: number): Collection<T> {
953
- if (typeof size === 'undefined') {
954
- return new Collection(this.#items.slice(start));
955
- }
956
-
957
- if (typeof start === 'undefined') {
958
- return new Collection(this.#items.slice(0, size));
959
- }
960
-
961
- return new Collection(this.#items.slice(start, start + size));
962
- }
963
-
964
- sliding(size: number, step: number = 1): Collection<Collection<T>> {
965
- const chunks = [];
966
- for (let i = 0; i < this.#items.length; i += step) {
967
- if (i + size > this.#items.length) {
968
- break;
969
- }
970
- chunks.push(this.#items.slice(i, i + size));
971
- }
972
-
973
- return new Collection(chunks.map(chunk => new Collection(chunk)));
974
- }
975
-
976
- sole(): T | null;
977
- sole<K extends keyof T>(key: K, value: T[K]): T | null;
978
- sole(callback: CollectionIteratorCallback<T, boolean>): T | null;
979
- sole<K extends keyof T>(keyOrCallback?: K | CollectionIteratorCallback<T, boolean>, value?: T[K]): T | null {
980
- if (typeof keyOrCallback === 'function') {
981
- const items = this.filter(keyOrCallback);
982
- return items.count() === 1 ? items.first() : null;
983
- }
984
-
985
- if (typeof keyOrCallback === 'string') {
986
- const items = this.where(keyOrCallback, value as T[K]);
987
- return items.count() === 1 ? items.first() : null;
988
- }
989
-
990
- return this.#items.length === 1 ? this.first() : null;
991
- }
992
-
993
- some(value: T): boolean;
994
- some(key: keyof T, value: T): boolean;
995
- some(callback: CollectionIteratorCallback<T, boolean>): boolean;
996
- some(...args: unknown[]): boolean {
997
- return this.contains(...args as [keyof T, T]);
998
- }
999
-
1000
- sort(compareFn?: CollectionSortCallback<T>): Collection<T> {
1001
- return new Collection(this.#items.toSorted(compareFn));
1002
- }
1003
-
1004
- sortBy<K extends keyof T>(key: K, order?: 'asc' | 'desc'): Collection<T>;
1005
- sortBy<K extends keyof T>(columns: [K, 'asc' | 'desc'][]): Collection<T>;
1006
- sortBy(callback: CollectionIteratorCallback<T, number>): Collection<T>;
1007
- sortBy(stack: ((a: T, b: T) => number)[]): Collection<T>;
1008
- sortBy<K extends keyof T>(
1009
- keyOrCallback: K | CollectionIteratorCallback<T, number> | [K, 'asc' | 'desc'][] | ((a: T, b: T) => number)[],
1010
- order: 'asc' | 'desc' = 'asc'
1011
- ): Collection<T> {
1012
- if (typeof keyOrCallback === 'function') {
1013
- let index = -1;
1014
- return new Collection(this.#items.toSorted((a, b) => {
1015
- index++;
1016
- return keyOrCallback(a, index, this) - keyOrCallback(b, index, this);
1017
- }));
1018
- }
1019
-
1020
- if (Array.isArray(keyOrCallback)) {
1021
- if (keyOrCallback.every((criteria) => Array.isArray(criteria))) {
1022
- return new Collection(this.#items.toSorted((a, b) => {
1023
- for (const [key, order] of keyOrCallback as [K, 'asc' | 'desc'][]) {
1024
- const va = a[key] ?? -Infinity;
1025
- const vb = b[key] ?? -Infinity;
1026
-
1027
- if (va > vb) {
1028
- return order === 'asc' ? 1 : -1;
1029
- }
1030
- if (va < vb) {
1031
- return order === 'asc' ? -1 : 1;
1032
- }
1033
- }
1034
- return 0;
1035
- }));
1036
- }
1037
-
1038
- return new Collection(this.#items.toSorted((a, b) => {
1039
- for (const sortFn of keyOrCallback as ((a: T, b: T) => number)[]) {
1040
- const result = sortFn(a, b);
1041
- if (result !== 0) {
1042
- return result;
1043
- }
1044
- }
1045
- return 0;
1046
- }));
1047
- }
1048
-
1049
- if (typeof keyOrCallback !== 'string') {
1050
- throw new TypeError('The key must be a string');
1051
- }
1052
-
1053
- return new Collection(this.#items.toSorted((a, b) => {
1054
- const va = a[keyOrCallback] ?? -Infinity;
1055
- const vb = b[keyOrCallback] ?? -Infinity;
1056
-
1057
- return va > vb
1058
- ? order === 'asc' ? 1 : -1
1059
- : (
1060
- va < vb
1061
- ? order === 'asc' ? -1 : 1
1062
- : 0
1063
- );
1064
-
1065
- }));
1066
- }
1067
-
1068
-
1069
- sortDesc(): Collection<T> {
1070
- return this.sort((a, b) => {
1071
- if (a > b) {
1072
- return -1;
1073
- }
1074
- if (a < b) {
1075
- return 1;
1076
- }
1077
- return 0;
1078
- });
1079
- }
1080
-
1081
- splice(start: number): Collection<T>;
1082
- splice(start: number, deleteCount: number): Collection<T>;
1083
- splice(start: number, deleteCount: number, ...items: T[]): Collection<T>;
1084
- splice(start: number, deleteCount?: number, ...items: T[]): Collection<T> {
1085
- const toDelete = deleteCount === undefined
1086
- ? this.#items.length
1087
- : deleteCount;
1088
-
1089
- const deleted = this.#items.splice(start, toDelete, ...items);
1090
-
1091
- emitChange(this);
1092
-
1093
- return new Collection<T>(deleted);
1094
- }
1095
-
1096
- split(groups: number): Collection<Collection<T>> {
1097
-
1098
- const result: Collection<T>[] = [];
1099
-
1100
- for (let i = 0; i < groups; i++) {
1101
- const chunk = this.#items.slice(
1102
- result.flat().length,
1103
- result.flat().length + Math.min(
1104
- Math.ceil((this.#items.length - result.flat().length) / (groups - i)),
1105
- this.#items.length - result.flat().length
1106
- )
1107
- );
1108
- result.push(new Collection(chunk));
1109
- }
1110
-
1111
- return new Collection(result);
1112
- }
1113
-
1114
- splitIn(groups: number): Collection<Collection<T>> {
1115
- const chunkSize = Math.ceil(this.#items.length / groups);
1116
-
1117
- return this.chunk(chunkSize);
1118
- }
1119
-
1120
-
1121
- sum(): number;
1122
- sum<K extends keyof T>(key: K): number;
1123
- sum<K extends keyof T>(key?: K): number {
1124
- if (typeof key === 'string') {
1125
- return this.#items.reduce((carry: number, item) => {
1126
- const value = item[key];
1127
- if (typeof value !== 'number') {
1128
- throw new TypeError('The items must be numbers');
1129
- }
1130
- return carry + value;
1131
- }, 0);
1132
- }
1133
-
1134
- return this.#items.reduce((carry: number, item) => {
1135
- if (typeof item !== 'number') {
1136
- throw new TypeError('The items must be numbers');
1137
- }
1138
- return carry + item;
1139
- }, 0);
1140
- }
1141
-
1142
-
1143
- take(amount: number): Collection<T> {
1144
- return new Collection(this.#items.slice(0, amount));
1145
- }
1146
-
1147
- takeUntil(value: T): Collection<T>;
1148
- takeUntil(callback: CollectionIteratorCallback<T, boolean>): Collection<T>;
1149
- takeUntil(valueOrCallback: T | CollectionIteratorCallback<T, boolean>): Collection<T> {
1150
- if (typeof valueOrCallback === 'function') {
1151
- return this.take(this.#items.findIndex((item, index) => {
1152
- return (valueOrCallback as CollectionIteratorCallback<T, boolean>)(item, index, this);
1153
- }));
1154
- }
1155
-
1156
- return this.take(this.#items.findIndex((item) => item == valueOrCallback));
1157
- }
1158
-
1159
- takeWhile(value: T): Collection<T>;
1160
- takeWhile(callback: CollectionIteratorCallback<T, boolean>): Collection<T>;
1161
- takeWhile(valueOrCallback: T | CollectionIteratorCallback<T, boolean>): Collection<T> {
1162
- if (typeof valueOrCallback === 'function') {
1163
- return this.take(this.#items.findIndex((item, index) => {
1164
- return !(valueOrCallback as CollectionIteratorCallback<T, boolean>)(item, index, this);
1165
- }));
1166
- }
1167
-
1168
- return this.take(this.#items.findIndex((item) => item != valueOrCallback));
1169
- }
1170
-
1171
- tap(callback: CollectionPipeCallback<T, void>): this {
1172
- callback(this);
1173
- return this;
1174
- }
1175
-
1176
- toArray(): T[] {
1177
- const convertImmediateChildrenToArray = (item: unknown): unknown => {
1178
- if (typeof item !== 'object') {
1179
- return item;
1180
- }
1181
-
1182
- if (Array.isArray(item)) {
1183
- return item.map(convertImmediateChildrenToArray);
1184
- }
1185
-
1186
- if (item && 'toArray' in item && typeof item.toArray === 'function') {
1187
- return item.toArray();
1188
- }
1189
-
1190
- if (item && 'toJson' in item && typeof item.toJson === 'function') {
1191
- return item.toJson();
1192
- }
1193
-
1194
- return item;
1195
- };
1196
-
1197
- return this.#items.map(convertImmediateChildrenToArray) as T[];
1198
- }
1199
-
1200
- toJson(): string {
1201
- return JSON.stringify(this.#items);
1202
- }
1203
-
1204
- transform<R>(callback: CollectionIteratorCallback<T, R>): Collection<T|R> {
1205
- for (const [index, item] of this.#items.entries()) {
1206
- (this.#items as (T|R)[]).splice(index, 1, callback(item, index, this));
1207
- }
1208
-
1209
- emitChange(this);
1210
- return this as unknown as Collection<T|R>;
1211
-
1212
- }
1213
-
1214
- unique(): Collection<T>;
1215
- unique<K extends keyof T>(key: K): Collection<T>;
1216
- unique<K extends keyof T>(key?: K): Collection<T> {
1217
- if (typeof key === 'string') {
1218
- return new Collection(
1219
- [...new Set(this.#items.map((item) => item[key]))]
1220
- .map((k) => this.#items.find((item) => item[key] == k) as T)
1221
- );
1222
- }
1223
-
1224
- return new Collection([...new Set(this.#items)]);
1225
- }
1226
-
1227
- uniqueStrict(): Collection<T>;
1228
- uniqueStrict<K extends keyof T>(key: K): Collection<T>;
1229
- uniqueStrict<K extends keyof T>(key?: K): Collection<T> {
1230
- if (typeof key === 'string') {
1231
- return new Collection(
1232
- [...new Set(this.#items.map((item) => item[key]))]
1233
- .map((k) => this.#items.find((item) => item[key] === k) as T)
1234
- );
1235
- }
1236
- return new Collection([...new Set(this.#items)]);
1237
- }
1238
-
1239
- unless(condition: boolean, callback: CollectionPipeCallback<T, void>, otherwise?: CollectionPipeCallback<T, void>): this {
1240
- if (!condition) {
1241
- callback(this);
1242
- } else if (typeof otherwise === 'function') {
1243
- otherwise(this);
1244
- }
1245
- return this;
1246
- }
1247
-
1248
- unlessEmpty(callback: CollectionPipeCallback<T, void>, otherwise?: CollectionPipeCallback<T, void>): this {
1249
- return this.whenNotEmpty(callback, otherwise);
1250
- }
1251
-
1252
- unlessNotEmpty(callback: CollectionPipeCallback<T, void>, otherwise?: CollectionPipeCallback<T, void>): this {
1253
- return this.whenEmpty(callback, otherwise);
1254
- }
1255
-
1256
- value<K extends keyof T>(key: K): T[K] | null {
1257
- if (this.#items.length === 0) {
1258
- return null;
1259
- }
1260
- return this.#items[0][key];
1261
- }
1262
-
1263
- when(condition: boolean, callback: CollectionPipeCallback<T, void>, otherwise?: CollectionPipeCallback<T, void>): this {
1264
- if (condition) {
1265
- callback(this);
1266
- } else if (typeof otherwise === 'function') {
1267
- otherwise(this);
1268
- }
1269
- return this;
1270
- }
1271
-
1272
- whenEmpty(callback: CollectionPipeCallback<T, void>, otherwise?: CollectionPipeCallback<T, void>): this {
1273
- if (this.isEmpty()) {
1274
- callback(this);
1275
- } else if (typeof otherwise === 'function') {
1276
- otherwise(this);
1277
- }
1278
- return this;
1279
- }
1280
-
1281
- whenNotEmpty(callback: CollectionPipeCallback<T, void>, otherwise?: CollectionPipeCallback<T, void>): this {
1282
- if (this.isNotEmpty()) {
1283
- callback(this);
1284
- } else if (typeof otherwise === 'function') {
1285
- otherwise(this);
1286
- }
1287
- return this;
1288
- }
1289
-
1290
- where<K extends keyof T>(key: K, value: T[K]): Collection<T>;
1291
- where<K extends keyof T>(key: K, operator: Operator, value: T[K]): Collection<T>;
1292
- where<K extends keyof T>(key: K, operator?: Operator | T[K], value?: T[K]): Collection<T> {
1293
- if (typeof value === 'undefined') {
1294
- return new Collection(this.#items.filter((item) => item[key] == operator));
1295
- }
1296
-
1297
- if (typeof operator !== 'string') {
1298
- throw new TypeError('The operator must be a string');
1299
- }
1300
-
1301
- if (value === null) {
1302
- return new Collection(this.#items.filter((item) => item[key] === null));
1303
- }
1304
-
1305
- const operatorMap: Record<Operator, CollectionIteratorCallback<T, boolean>> = {
1306
- '=': (item) => item[key] == value,
1307
- '!=': (item) => item[key] != value,
1308
- '>': (item) => item[key] > value,
1309
- '<': (item) => item[key] < value,
1310
- '>=': (item) => item[key] >= value,
1311
- '<=': (item) => item[key] <= value,
1312
- };
1313
-
1314
- if (!(operator in operatorMap)) {
1315
- throw new Error('Unsupported operator');
1316
- }
1317
-
1318
- return new Collection(this.#items.filter((value, index) => {
1319
- return operatorMap[operator as Operator](value, index, this);
1320
- }));
1321
- }
1322
-
1323
- whereStrict<K extends keyof T>(key: K, value: T[K]): Collection<T>;
1324
- whereStrict<K extends keyof T>(key: K, operator: Operator, value: T[K]): Collection<T>;
1325
- whereStrict<K extends keyof T>(key: K, operator?: Operator | T[K], value?: T[K]): Collection<T> {
1326
- if (typeof value === 'undefined') {
1327
- return new Collection(this.#items.filter((item) => item[key] === operator));
1328
- }
1329
-
1330
- if (typeof operator !== 'string') {
1331
- throw new TypeError('The operator must be a string');
1332
- }
1333
-
1334
- if (value === null) {
1335
- return new Collection(this.#items.filter((item) => item[key] === null));
1336
- }
1337
-
1338
- const operatorMap: Record<Operator, CollectionIteratorCallback<T, boolean>> = {
1339
- '=': (item) => item[key] === value,
1340
- '!=': (item) => item[key] !== value,
1341
- '>': (item) => item[key] > value,
1342
- '<': (item) => item[key] < value,
1343
- '>=': (item) => item[key] >= value,
1344
- '<=': (item) => item[key] <= value,
1345
- };
1346
-
1347
- if (!(operator in operatorMap)) {
1348
- throw new Error('Unsupported operator');
1349
- }
1350
-
1351
- return new Collection(this.#items.filter((value, index) => {
1352
- return operatorMap[operator as Operator](value, index, this);
1353
- }));
1354
- }
1355
-
1356
- whereBetween<K extends keyof T>(key: K, [min, max]: [T[K], T[K]]): Collection<T> {
1357
- return new Collection(this.#items.filter((item) => item[key] >= min && item[key] <= max));
1358
- }
1359
-
1360
- whereIn<K extends keyof T>(key: K, values: T[K][]): Collection<T> {
1361
- return new Collection(this.#items.filter((item) => values.includes(item[key])));
1362
- }
1363
-
1364
- whereInstanceOf<R extends Constructor<T>>(constructor: R): Collection<T> {
1365
- return new Collection(this.#items.filter((item) => item instanceof constructor));
1366
- }
1367
-
1368
- whereNotBetween<K extends keyof T>(key: K, [min, max]: [T[K], T[K]]): Collection<T> {
1369
- return new Collection(this.#items.filter((item) => item[key] < min || item[key] > max));
1370
- }
1371
-
1372
- whereNotIn<K extends keyof T>(key: K, values: T[K][]): Collection<T> {
1373
- return new Collection(this.#items.filter((item) => !values.includes(item[key])));
1374
- }
1375
-
1376
- whereNotNull<K extends keyof T>(key: K): Collection<T> {
1377
- return new Collection(this.#items.filter((item) => item[key] !== null));
1378
- }
1379
-
1380
- whereNull<K extends keyof T>(key: K): Collection<T> {
1381
- return new Collection(this.#items.filter((item) => item[key] === null));
1382
- }
1383
-
1384
- zip<R>(items: Collection<R> | R[]): Collection<[T, NonNullable<R> | null]> {
1385
- if (!Array.isArray(items)) {
1386
- return new Collection(
1387
- this.#items.map((item, index) => [item, items.get(index)]) as [T, NonNullable<R> | null][]
1388
- );
1389
- }
1390
-
1391
- return new Collection(
1392
- this.#items.map((item, index) => [item, items[index] ?? null])
1393
- );
1394
- }
1395
-
1396
-
1397
- }
1398
-