• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IlyasDeckers / ody-core / 13589380408

28 Feb 2025 01:41PM UTC coverage: 13.422% (-19.7%) from 33.151%
13589380408

push

github

IlyasDeckers
add support classes/methods (wip)

0 of 2412 new or added lines in 16 files covered. (0.0%)

544 of 4053 relevant lines covered (13.42%)

4.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Support/Collection/Collection.php
1
<?php
2

3
declare(strict_types=1);
4

5

6
namespace Ody\Core\Support\Collection;
7

8
use ArrayAccess;
9
use ArrayIterator;
10
use Closure;
11
use Ody\Core\Support\Collection\Traits\EnumeratesValues;
12
use Hyperf\Contract\Arrayable;
13
use Hyperf\Contract\CanBeEscapedWhenCastToString;
14
use Hyperf\Contract\Jsonable;
15
use Hyperf\Macroable\Macroable;
16
use Hyperf\Stringable\Stringable;
17
use InvalidArgumentException;
18
use JsonSerializable;
19
use stdClass;
20
use Traversable;
21

22
/**
23
 * Most of the methods in this file come from illuminate/collections,
24
 * thanks Laravel Team provide such a useful class.
25
 *
26
 * @template TKey of array-key
27
 * @template TValue
28
 *
29
 * @implements ArrayAccess<TKey, TValue>
30
 * @implements Enumerable<TKey, TValue>
31
 */
32
class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
33
{
34
    /**
35
     * @use EnumeratesValues<TKey, TValue>
36
     */
37
    use EnumeratesValues;
38

39
    use Macroable;
40

41
    /**
42
     * The items contained in the collection.
43
     *
44
     * @var array<TKey, TValue>
45
     */
46
    protected array $items = [];
47

48
    /**
49
     * Create a new collection.
50
     * @param null|Arrayable<TKey,TValue>|iterable<TKey,TValue>|Jsonable|JsonSerializable $items
51
     */
NEW
52
    public function __construct($items = [])
×
53
    {
NEW
54
        $this->items = $this->getArrayableItems($items);
×
55
    }
56

57
    /**
58
     * @param null|Arrayable<TKey,TValue>|iterable<TKey, TValue>|Jsonable|JsonSerializable $items
59
     * @return static<TKey, TValue>
60
     */
NEW
61
    public function fill($items = [])
×
62
    {
NEW
63
        $this->items = $this->getArrayableItems($items);
×
NEW
64
        return $this;
×
65
    }
66

67
    /**
68
     * Get all of the items in the collection.
69
     *
70
     * @return array<TKey, TValue>
71
     */
NEW
72
    public function all(): array
×
73
    {
NEW
74
        return $this->items;
×
75
    }
76

77
    /**
78
     * Get the median of a given key.
79
     *
80
     * @param null|array<array-key, string>|string $key
81
     * @return null|float|int
82
     */
NEW
83
    public function median($key = null)
×
84
    {
NEW
85
        $values = (isset($key) ? $this->pluck($key) : $this)->filter(function ($item) {
×
NEW
86
            return ! is_null($item);
×
NEW
87
        })->sort()->values();
×
NEW
88
        $count = $values->count();
×
NEW
89
        if ($count == 0) {
×
NEW
90
            return null;
×
91
        }
NEW
92
        $middle = (int) ($count / 2);
×
NEW
93
        if ($count % 2) {
×
NEW
94
            return $values->get($middle);
×
95
        }
NEW
96
        return (new static([
×
NEW
97
            $values->get($middle - 1),
×
NEW
98
            $values->get($middle),
×
NEW
99
        ]))->average();
×
100
    }
101

102
    /**
103
     * Get the mode of a given key.
104
     *
105
     * @param null|array<array-key, string>|string $key
106
     * @return null|array<int, float|int>
107
     */
NEW
108
    public function mode($key = null)
×
109
    {
NEW
110
        if ($this->count() == 0) {
×
NEW
111
            return null;
×
112
        }
NEW
113
        $collection = isset($key) ? $this->pluck($key) : $this;
×
114

115
        /**
116
         * @template TValue of array-key
117
         * @var static<TValue, int> $counts
118
         */
NEW
119
        $counts = new self();
×
NEW
120
        $collection->each(function ($value) use ($counts) {
×
NEW
121
            $counts->offsetSet($value, isset($counts[$value]) ? $counts[$value] + 1 : 1);
×
NEW
122
        });
×
NEW
123
        $sorted = $counts->sort();
×
NEW
124
        $highestValue = $sorted->last();
×
NEW
125
        return $sorted->filter(function ($value) use ($highestValue) {
×
NEW
126
            return $value == $highestValue;
×
NEW
127
        })->sort()->keys()->all();
×
128
    }
129

130
    /**
131
     * Collapse the collection of items into a single array.
132
     *
133
     * @return static<int, mixed>
134
     */
NEW
135
    public function collapse(): Enumerable
×
136
    {
NEW
137
        return new static(Arr::collapse($this->items));
×
138
    }
139

140
    /**
141
     * Determine if an item exists in the collection.
142
     *
143
     * @param null|mixed $operator
144
     * @param null|mixed $value
145
     * @param (callable(TValue): bool)|string|TValue $key
146
     */
NEW
147
    public function contains($key, $operator = null, $value = null): bool
×
148
    {
NEW
149
        if (func_num_args() === 1) {
×
NEW
150
            if ($this->useAsCallable($key)) {
×
NEW
151
                $placeholder = new stdClass();
×
NEW
152
                return $this->first($key, $placeholder) !== $placeholder;
×
153
            }
NEW
154
            return in_array($key, $this->items);
×
155
        }
NEW
156
        return $this->contains($this->operatorForWhere(...func_get_args()));
×
157
    }
158

159
    /**
160
     * Determine if the collection contains a single item.
161
     */
NEW
162
    public function containsOneItem(): bool
×
163
    {
NEW
164
        return $this->count() === 1;
×
165
    }
166

167
    /**
168
     * Determine if an item exists in the collection using strict comparison.
169
     *
170
     * @param null|TValue $value
171
     * @param callable|TKey|TValue $key
172
     */
NEW
173
    public function containsStrict($key, $value = null): bool
×
174
    {
NEW
175
        if (func_num_args() === 2) {
×
NEW
176
            return $this->contains(function ($item) use ($key, $value) {
×
NEW
177
                return data_get($item, $key) === $value;
×
NEW
178
            });
×
179
        }
NEW
180
        if ($this->useAsCallable($key)) {
×
NEW
181
            return ! is_null($this->first($key));
×
182
        }
NEW
183
        return in_array($key, $this->items, true);
×
184
    }
185

186
    /**
187
     * Cross join with the given lists, returning all possible permutations.
188
     */
NEW
189
    public function crossJoin(...$lists): static
×
190
    {
NEW
191
        return new static(Arr::crossJoin($this->items, ...array_map([$this, 'getArrayableItems'], $lists)));
×
192
    }
193

194
    /**
195
     * Determine if an item is not contained in the collection.
196
     *
197
     * @param null|mixed $operator
198
     * @param null|mixed $value
199
     * @param (callable(TValue): bool)|string|TValue $key
200
     */
NEW
201
    public function doesntContain($key, $operator = null, $value = null): bool
×
202
    {
NEW
203
        return ! $this->contains(...func_get_args());
×
204
    }
205

206
    /**
207
     * Flatten a multi-dimensional associative array with dots.
208
     *
209
     * @return static<TKey, TValue>
210
     */
NEW
211
    public function dot(): static
×
212
    {
NEW
213
        return new static(Arr::dot($this->all()));
×
214
    }
215

216
    /**
217
     * Convert a flatten "dot" notation array into an expanded array.
218
     */
NEW
219
    public function undot(): static
×
220
    {
NEW
221
        return new static(Arr::undot($this->all()));
×
222
    }
223

224
    /**
225
     * Get the items in the collection that are not present in the given items.
226
     *
227
     * @param Arrayable<array-key, TValue>|iterable<array-key, TValue> $items
228
     * @return static<TKey, TValue>
229
     */
NEW
230
    public function diff($items): static
×
231
    {
NEW
232
        return new static(array_diff($this->items, $this->getArrayableItems($items)));
×
233
    }
234

235
    /**
236
     * Get the items in the collection that are not present in the given items.
237
     *
238
     * @param Arrayable<array-key, TValue>|iterable<array-key, TValue> $items
239
     * @param callable(TValue): int $callback
240
     * @return static<TKey, TValue>
241
     */
NEW
242
    public function diffUsing($items, callable $callback): static
×
243
    {
NEW
244
        return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
×
245
    }
246

247
    /**
248
     * Get the items in the collection whose keys and values are not present in the given items.
249
     *
250
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
251
     * @return static<TKey, TValue>
252
     */
NEW
253
    public function diffAssoc($items): static
×
254
    {
NEW
255
        return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
×
256
    }
257

258
    /**
259
     * Get the items in the collection whose keys and values are not present in the given items.
260
     *
261
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
262
     * @param callable(TKey): int $callback
263
     * @return static<TKey, TValue>
264
     */
NEW
265
    public function diffAssocUsing($items, callable $callback): static
×
266
    {
NEW
267
        return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
×
268
    }
269

270
    /**
271
     * Get the items in the collection whose keys are not present in the given items.
272
     *
273
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
274
     * @return static<TKey, TValue>
275
     */
NEW
276
    public function diffKeys($items): static
×
277
    {
NEW
278
        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
×
279
    }
280

281
    /**
282
     * Get the items in the collection whose keys are not present in the given items.
283
     *
284
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
285
     * @param callable(TKey): int $callback
286
     * @return static<TKey, TValue>
287
     */
NEW
288
    public function diffKeysUsing($items, callable $callback): static
×
289
    {
NEW
290
        return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
×
291
    }
292

293
    /**
294
     * Get all items except for those with the specified keys.
295
     *
296
     * @param null|array<array-key, TKey>|static<array-key, TKey> $keys
297
     * @return static<TKey, TValue>
298
     */
NEW
299
    public function except($keys): static
×
300
    {
NEW
301
        if (is_null($keys)) {
×
NEW
302
            return new static($this->items);
×
303
        }
NEW
304
        if ($keys instanceof self) {
×
NEW
305
            $keys = $keys->all();
×
NEW
306
        } elseif (! is_array($keys)) {
×
NEW
307
            $keys = func_get_args();
×
308
        }
NEW
309
        return new static(Arr::except($this->items, $keys));
×
310
    }
311

312
    /**
313
     * Run a filter over each of the items.
314
     *
315
     * @param null|(callable(TValue, TKey): bool) $callback
316
     * @return static<TKey, TValue>
317
     */
NEW
318
    public function filter(?callable $callback = null): static
×
319
    {
NEW
320
        if ($callback) {
×
NEW
321
            return new static(Arr::where($this->items, $callback));
×
322
        }
NEW
323
        return new static(array_filter($this->items));
×
324
    }
325

326
    /**
327
     * Get the first item from the collection.
328
     *
329
     * @template TFirstDefault
330
     *
331
     * @param null|(callable(TValue, TKey): bool) $callback
332
     * @param (Closure(): TFirstDefault)|TFirstDefault $default
333
     * @return TFirstDefault|TValue
334
     */
NEW
335
    public function first(?callable $callback = null, $default = null)
×
336
    {
NEW
337
        return Arr::first($this->items, $callback, $default);
×
338
    }
339

340
    /**
341
     * Get the first item by the given key value pair.
342
     *
343
     * @return null|TValue
344
     */
NEW
345
    public function firstWhere(callable|string $key, mixed $operator = null, mixed $value = null): mixed
×
346
    {
NEW
347
        return $this->first($this->operatorForWhere(...func_get_args()));
×
348
    }
349

350
    /**
351
     * Get a flattened array of the items in the collection.
352
     *
353
     * @param float|int $depth
354
     * @return static<int, mixed>
355
     */
NEW
356
    public function flatten($depth = INF): Enumerable
×
357
    {
NEW
358
        return new static(Arr::flatten($this->items, $depth));
×
359
    }
360

361
    /**
362
     * Flip the items in the collection.
363
     *
364
     * @return static<TKey, TValue>
365
     */
NEW
366
    public function flip(): Enumerable
×
367
    {
NEW
368
        return new static(array_flip($this->items));
×
369
    }
370

371
    /**
372
     * Remove an item from the collection by key.
373
     *
374
     * @param Arrayable<array-key, TValue>|iterable<array-key, TKey>|TKey $keys
375
     * @return $this
376
     */
NEW
377
    public function forget($keys): static
×
378
    {
NEW
379
        foreach ($this->getArrayableItems($keys) as $key) {
×
NEW
380
            $this->offsetUnset($key);
×
381
        }
NEW
382
        return $this;
×
383
    }
384

385
    /**
386
     * Get an item from the collection by key.
387
     *
388
     * @template TGetDefault
389
     *
390
     * @param TKey $key
391
     * @param (Closure(): TGetDefault)|TGetDefault $default
392
     * @return TGetDefault|TValue
393
     */
NEW
394
    public function get($key, $default = null)
×
395
    {
NEW
396
        if ($this->offsetExists($key)) {
×
NEW
397
            return $this->items[$key];
×
398
        }
NEW
399
        return value($default);
×
400
    }
401

402
    /**
403
     * Get an item from the collection by key or add it to collection if it does not exist.
404
     *
405
     * @template TGetOrPutValue
406
     *
407
     * @param (Closure(): TGetOrPutValue)|TGetOrPutValue $value
408
     * @return TGetOrPutValue|TValue
409
     */
NEW
410
    public function getOrPut(int|string $key, mixed $value): mixed
×
411
    {
NEW
412
        if (array_key_exists($key, $this->items)) {
×
NEW
413
            return $this->items[$key];
×
414
        }
415

NEW
416
        $this->offsetSet($key, $value = value($value));
×
417

NEW
418
        return $value;
×
419
    }
420

421
    /**
422
     * Get an item from the collection by key or add it to collection if it does not exist.
423
     *
424
     * @template TGetOrPutValue
425
     *
426
     * @param (Closure(): TGetOrPutValue)|TGetOrPutValue $value
427
     * @return TGetOrPutValue|TValue
428
     */
NEW
429
    public function getOrSet(int|string $key, mixed $value): mixed
×
430
    {
NEW
431
        return $this->getOrPut($key, $value);
×
432
    }
433

434
    /**
435
     * Group an associative array by a field or using a callback.
436
     * @param mixed $groupBy
437
     */
NEW
438
    public function groupBy($groupBy, bool $preserveKeys = false): Enumerable
×
439
    {
NEW
440
        if (is_array($groupBy)) {
×
NEW
441
            $nextGroups = $groupBy;
×
NEW
442
            $groupBy = array_shift($nextGroups);
×
443
        }
NEW
444
        $groupBy = $this->valueRetriever($groupBy);
×
NEW
445
        $results = [];
×
NEW
446
        foreach ($this->items as $key => $value) {
×
NEW
447
            $groupKeys = $groupBy($value, $key);
×
NEW
448
            if (! is_array($groupKeys)) {
×
NEW
449
                $groupKeys = [$groupKeys];
×
450
            }
NEW
451
            foreach ($groupKeys as $groupKey) {
×
NEW
452
                $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
×
NEW
453
                if (! array_key_exists($groupKey, $results)) {
×
NEW
454
                    $results[$groupKey] = new static();
×
455
                }
NEW
456
                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
×
457
            }
458
        }
NEW
459
        $result = new static($results);
×
NEW
460
        if (! empty($nextGroups)) {
×
NEW
461
            return $result->map->groupBy($nextGroups, $preserveKeys);
×
462
        }
NEW
463
        return $result;
×
464
    }
465

466
    /**
467
     * Key an associative array by a field or using a callback.
468
     *
469
     * @param array|(callable(TValue, TKey): array-key)|string $keyBy
470
     * @return static<TKey, TValue>
471
     */
NEW
472
    public function keyBy($keyBy): static
×
473
    {
NEW
474
        $keyBy = $this->valueRetriever($keyBy);
×
NEW
475
        $results = [];
×
NEW
476
        foreach ($this->items as $key => $item) {
×
NEW
477
            $resolvedKey = $keyBy($item, $key);
×
NEW
478
            if (is_object($resolvedKey)) {
×
NEW
479
                $resolvedKey = (string) $resolvedKey;
×
480
            }
NEW
481
            $results[$resolvedKey] = $item;
×
482
        }
NEW
483
        return new static($results);
×
484
    }
485

486
    /**
487
     * Determine if an item exists in the collection by key.
488
     * @param array<array-key, TKey>|TKey $key
489
     */
NEW
490
    public function has($key): bool
×
491
    {
NEW
492
        $keys = is_array($key) ? $key : func_get_args();
×
NEW
493
        foreach ($keys as $value) {
×
NEW
494
            if (! $this->offsetExists($value)) {
×
NEW
495
                return false;
×
496
            }
497
        }
NEW
498
        return true;
×
499
    }
500

501
    /**
502
     * Determine if any of the keys exist in the collection.
503
     *
504
     * @param array<array-key, TKey>|TKey $key
505
     */
NEW
506
    public function hasAny($key): bool
×
507
    {
NEW
508
        if ($this->isEmpty()) {
×
NEW
509
            return false;
×
510
        }
511

NEW
512
        $keys = is_array($key) ? $key : func_get_args();
×
513

NEW
514
        foreach ($keys as $value) {
×
NEW
515
            if ($this->has($value)) {
×
NEW
516
                return true;
×
517
            }
518
        }
519

NEW
520
        return false;
×
521
    }
522

523
    /**
524
     * Concatenate values of a given key as a string.
525
     */
NEW
526
    public function implode(array|callable|string $value, ?string $glue = null): string
×
527
    {
NEW
528
        if ($this->useAsCallable($value)) {
×
NEW
529
            return implode($glue ?? '', $this->map($value)->all());
×
530
        }
531

NEW
532
        $first = $this->first();
×
533

NEW
534
        if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
×
NEW
535
            return implode($glue ?? '', $this->pluck($value)->all());
×
536
        }
537

NEW
538
        return implode($value ?: '', $this->items);
×
539
    }
540

541
    /**
542
     * Intersect the collection with the given items.
543
     *
544
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
545
     * @return static<TKey, TValue>
546
     */
NEW
547
    public function intersect(mixed $items): static
×
548
    {
NEW
549
        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
×
550
    }
551

552
    /**
553
     * Intersect the collection with the given items with additional index check.
554
     *
555
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
556
     * @return static<TKey, TValue>
557
     */
NEW
558
    public function intersectAssoc($items): static
×
559
    {
NEW
560
        return new static(array_intersect_assoc($this->items, $this->getArrayableItems($items)));
×
561
    }
562

563
    /**
564
     * Intersect the collection with the given items by key.
565
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
566
     * @return static<TKey, TValue>
567
     */
NEW
568
    public function intersectByKeys($items): static
×
569
    {
NEW
570
        return new static(array_intersect_key($this->items, $this->getArrayableItems($items)));
×
571
    }
572

573
    /**
574
     * Determine if the collection is empty or not.
575
     */
NEW
576
    public function isEmpty(): bool
×
577
    {
NEW
578
        return empty($this->items);
×
579
    }
580

581
    /**
582
     * Get the keys of the collection items.
583
     * @return static<int, TKey>
584
     */
NEW
585
    public function keys(): Enumerable
×
586
    {
NEW
587
        return new static(array_keys($this->items));
×
588
    }
589

590
    /**
591
     * Get the last item from the collection.
592
     *
593
     * @template TLastDefault
594
     *
595
     * @param null|(callable(TValue, TKey): bool) $callback
596
     * @param (Closure(): TLastDefault)|TLastDefault $default
597
     * @return TLastDefault|TValue
598
     */
NEW
599
    public function last(?callable $callback = null, $default = null)
×
600
    {
NEW
601
        return Arr::last($this->items, $callback, $default);
×
602
    }
603

604
    /**
605
     * Get the values of a given key.
606
     *
607
     * @param array<array-key, string>|string $value
608
     * @return static<int, mixed>
609
     */
NEW
610
    public function pluck(array|string $value, ?string $key = null): Enumerable
×
611
    {
NEW
612
        return new static(Arr::pluck($this->items, $value, $key));
×
613
    }
614

615
    /**
616
     * Run a map over each of the items.
617
     *
618
     * @template TMapValue
619
     *
620
     * @param callable(TValue, TKey): TMapValue $callback
621
     * @return static<TKey, TMapValue>
622
     */
NEW
623
    public function map(callable $callback): Enumerable
×
624
    {
NEW
625
        $result = [];
×
NEW
626
        foreach ($this->items as $key => $value) {
×
NEW
627
            $result[$key] = $callback($value, $key);
×
628
        }
629

NEW
630
        return new static($result);
×
631
    }
632

633
    /**
634
     * Run a dictionary map over the items.
635
     * The callback should return an associative array with a single key/value pair.
636
     *
637
     * @template TMapToDictionaryKey of array-key
638
     * @template TMapToDictionaryValue
639
     *
640
     * @param callable(TValue, TKey): array<TMapToDictionaryKey, TMapToDictionaryValue> $callback
641
     * @return static<TMapToDictionaryKey, array<int, TMapToDictionaryValue>>
642
     */
NEW
643
    public function mapToDictionary(callable $callback): Enumerable
×
644
    {
NEW
645
        $dictionary = [];
×
NEW
646
        foreach ($this->items as $key => $item) {
×
NEW
647
            $pair = $callback($item, $key);
×
NEW
648
            $key = key($pair);
×
NEW
649
            $value = reset($pair);
×
NEW
650
            if (! isset($dictionary[$key])) {
×
NEW
651
                $dictionary[$key] = [];
×
652
            }
NEW
653
            $dictionary[$key][] = $value;
×
654
        }
NEW
655
        return new static($dictionary);
×
656
    }
657

658
    /**
659
     * Run an associative map over each of the items.
660
     * The callback should return an associative array with a single key/value pair.
661
     *
662
     * @template TMapWithKeysKey of array-key
663
     * @template TMapWithKeysValue
664
     *
665
     * @param callable(TValue, TKey): array<TMapWithKeysKey, TMapWithKeysValue> $callback
666
     * @return static<TMapWithKeysKey, TMapWithKeysValue>
667
     */
NEW
668
    public function mapWithKeys(callable $callback): Enumerable
×
669
    {
NEW
670
        return new static(Arr::mapWithKeys($this->items, $callback));
×
671
    }
672

673
    /**
674
     * Merge the collection with the given items.
675
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
676
     * @return static<TKey, TValue>
677
     */
NEW
678
    public function merge($items): static
×
679
    {
NEW
680
        return new static(array_merge($this->items, $this->getArrayableItems($items)));
×
681
    }
682

683
    /**
684
     * Recursively merge the collection with the given items.
685
     *
686
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
687
     * @return static<TKey, TValue>
688
     */
NEW
689
    public function mergeRecursive($items): static
×
690
    {
NEW
691
        return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
×
692
    }
693

694
    /**
695
     * Create a collection by using this collection for keys and another for its values.
696
     *
697
     * @template TCombineValue
698
     *
699
     * @param Arrayable<array-key, TCombineValue>|iterable<array-key, TCombineValue> $values
700
     * @return static<TKey, TCombineValue>
701
     */
NEW
702
    public function combine($values): static
×
703
    {
NEW
704
        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
×
705
    }
706

707
    /**
708
     * Union the collection with the given items.
709
     *
710
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
711
     * @return static<TKey, TValue>
712
     */
NEW
713
    public function union($items): static
×
714
    {
NEW
715
        return new static($this->items + $this->getArrayableItems($items));
×
716
    }
717

718
    /**
719
     * Create a new collection consisting of every n-th element.
720
     *
721
     * @return static<TKey, TValue>
722
     */
NEW
723
    public function nth(int $step, int $offset = 0): static
×
724
    {
NEW
725
        $new = [];
×
NEW
726
        $position = 0;
×
NEW
727
        foreach ($this->items as $item) {
×
NEW
728
            if ($position % $step === $offset) {
×
NEW
729
                $new[] = $item;
×
730
            }
NEW
731
            ++$position;
×
732
        }
NEW
733
        return new static($new);
×
734
    }
735

736
    /**
737
     * Get the items with the specified keys.
738
     *
739
     * @param null|array<array-key, TKey>|static<array-key, TKey>|string $keys
740
     * @return static<TKey, TValue>
741
     */
NEW
742
    public function only($keys): static
×
743
    {
NEW
744
        if (is_null($keys)) {
×
NEW
745
            return new static($this->items);
×
746
        }
NEW
747
        if ($keys instanceof self) {
×
NEW
748
            $keys = $keys->all();
×
749
        }
NEW
750
        $keys = is_array($keys) ? $keys : func_get_args();
×
NEW
751
        return new static(Arr::only($this->items, $keys));
×
752
    }
753

754
    /**
755
     * Get and remove the last item from the collection.
756
     */
NEW
757
    public function pop()
×
758
    {
NEW
759
        return array_pop($this->items);
×
760
    }
761

762
    /**
763
     * Push an item onto the beginning of the collection.
764
     *
765
     * @param TValue $value
766
     * @param null|TKey $key
767
     * @return $this
768
     */
NEW
769
    public function prepend($value, $key = null): static
×
770
    {
NEW
771
        $this->items = Arr::prepend($this->items, $value, $key);
×
NEW
772
        return $this;
×
773
    }
774

775
    /**
776
     * Push an item onto the end of the collection.
777
     *
778
     * @param TValue $value
779
     * @return $this
780
     */
NEW
781
    public function push($value): static
×
782
    {
NEW
783
        $this->offsetSet(null, $value);
×
NEW
784
        return $this;
×
785
    }
786

787
    /**
788
     * Push all of the given items onto the collection.
789
     *
790
     * @param iterable<array-key, TValue> $source
791
     * @return static<TKey, TValue>
792
     */
NEW
793
    public function concat($source): static
×
794
    {
NEW
795
        $result = new static($this);
×
NEW
796
        foreach ($source as $item) {
×
NEW
797
            $result->push($item);
×
798
        }
NEW
799
        return $result;
×
800
    }
801

802
    /**
803
     * Get and remove an item from the collection.
804
     *
805
     * @template TPullDefault
806
     *
807
     * @param TKey $key
808
     * @param (Closure(): TPullDefault)|TPullDefault $default
809
     * @return TPullDefault|TValue
810
     */
NEW
811
    public function pull($key, $default = null)
×
812
    {
NEW
813
        return Arr::pull($this->items, $key, $default);
×
814
    }
815

816
    /**
817
     * Put an item in the collection by key.
818
     *
819
     * @param TKey $key
820
     * @param TValue $value
821
     * @return $this
822
     */
NEW
823
    public function put($key, $value): static
×
824
    {
NEW
825
        $this->offsetSet($key, $value);
×
NEW
826
        return $this;
×
827
    }
828

829
    /**
830
     * Get one or a specified number of items randomly from the collection.
831
     *
832
     * @return static<int, TValue>|TValue
833
     * @throws InvalidArgumentException
834
     */
NEW
835
    public function random(?int $number = null)
×
836
    {
NEW
837
        if (is_null($number)) {
×
NEW
838
            return Arr::random($this->items);
×
839
        }
NEW
840
        return new static(Arr::random($this->items, $number));
×
841
    }
842

843
    /**
844
     * Create a collection with the given range.
845
     *
846
     * @return static<int, int>
847
     */
NEW
848
    public static function range(float|int|string $from, float|int|string $to): static
×
849
    {
NEW
850
        return new static(range($from, $to));
×
851
    }
852

853
    /**
854
     * Replace the collection items with the given items.
855
     *
856
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
857
     * @return static
858
     */
NEW
859
    public function replace($items)
×
860
    {
NEW
861
        return new static(array_replace($this->items, $this->getArrayableItems($items)));
×
862
    }
863

864
    /**
865
     * Recursively replace the collection items with the given items.
866
     *
867
     * @param Arrayable<TKey, TValue>|iterable<TKey, TValue> $items
868
     * @return static
869
     */
NEW
870
    public function replaceRecursive($items)
×
871
    {
NEW
872
        return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
×
873
    }
874

875
    /**
876
     * Reverse items order.
877
     *
878
     * @return static<TKey, TValue>
879
     */
NEW
880
    public function reverse(): static
×
881
    {
NEW
882
        return new static(array_reverse($this->items, true));
×
883
    }
884

885
    /**
886
     * Search the collection for a given value and return the corresponding key if successful.
887
     *
888
     * @param (callable(TValue,TKey): bool)|TValue $value
889
     * @return bool|TKey
890
     */
NEW
891
    public function search($value, bool $strict = false)
×
892
    {
NEW
893
        if (! $this->useAsCallable($value)) {
×
NEW
894
            return array_search($value, $this->items, $strict);
×
895
        }
NEW
896
        foreach ($this->items as $key => $item) {
×
NEW
897
            if (call_user_func($value, $item, $key)) {
×
NEW
898
                return $key;
×
899
            }
900
        }
NEW
901
        return false;
×
902
    }
903

904
    /**
905
     * Get the item before the given item.
906
     *
907
     * @param (callable(TValue,TKey): bool)|TValue $value
908
     * @return null|TValue
909
     */
NEW
910
    public function before(mixed $value, bool $strict = false): mixed
×
911
    {
NEW
912
        $key = $this->search($value, $strict);
×
913

NEW
914
        if ($key === false) {
×
NEW
915
            return null;
×
916
        }
917

NEW
918
        $position = $this->keys()->search($key);
×
919

NEW
920
        if ($position === 0) {
×
NEW
921
            return null;
×
922
        }
923

NEW
924
        return $this->get($this->keys()->get($position - 1));
×
925
    }
926

927
    /**
928
     * Get the item after the given item.
929
     *
930
     * @param (callable(TValue,TKey): bool)|TValue $value
931
     * @return null|TValue
932
     */
NEW
933
    public function after(mixed $value, bool $strict = false): mixed
×
934
    {
NEW
935
        $key = $this->search($value, $strict);
×
936

NEW
937
        if ($key === false) {
×
NEW
938
            return null;
×
939
        }
940

NEW
941
        $position = $this->keys()->search($key);
×
942

NEW
943
        if ($position === $this->keys()->count() - 1) {
×
NEW
944
            return null;
×
945
        }
946

NEW
947
        return $this->get($this->keys()->get($position + 1));
×
948
    }
949

950
    /**
951
     * Get and remove the first item from the collection.
952
     *
953
     * @return null|TValue
954
     */
NEW
955
    public function shift()
×
956
    {
NEW
957
        return array_shift($this->items);
×
958
    }
959

960
    /**
961
     * Shuffle the items in the collection.
962
     *
963
     * @return static<TKey, TValue>
964
     */
NEW
965
    public function shuffle(?int $seed = null): static
×
966
    {
NEW
967
        return new static(Arr::shuffle($this->items, $seed));
×
968
    }
969

970
    /**
971
     * Skip the first {$count} items.
972
     *
973
     * @return static<TKey, TValue>
974
     */
NEW
975
    public function skip(int $count): static
×
976
    {
NEW
977
        return $this->slice($count);
×
978
    }
979

980
    /**
981
     * Slice the underlying collection array.
982
     *
983
     * @return static<TKey, TValue>
984
     */
NEW
985
    public function slice(int $offset, ?int $length = null): static
×
986
    {
NEW
987
        return new static(array_slice($this->items, $offset, $length, true));
×
988
    }
989

990
    /**
991
     * Create chunks representing a "sliding window" view of the items in the collection.
992
     *
993
     * @return static<int, static>
994
     */
NEW
995
    public function sliding(int $size = 2, int $step = 1): static
×
996
    {
NEW
997
        $chunks = (int) floor(($this->count() - $size) / $step) + 1;
×
998

NEW
999
        return static::times($chunks, fn ($number) => $this->slice(($number - 1) * $step, $size));
×
1000
    }
1001

1002
    /**
1003
     * Split a collection into a certain number of groups.
1004
     *
1005
     * @return static<int, static<TKey, TValue>>
1006
     */
NEW
1007
    public function split(int $numberOfGroups): static
×
1008
    {
NEW
1009
        if ($this->isEmpty()) {
×
NEW
1010
            return new static();
×
1011
        }
NEW
1012
        $groups = new static();
×
NEW
1013
        $groupSize = (int) floor($this->count() / $numberOfGroups);
×
NEW
1014
        $remain = $this->count() % $numberOfGroups;
×
NEW
1015
        $start = 0;
×
NEW
1016
        for ($i = 0; $i < $numberOfGroups; ++$i) {
×
NEW
1017
            $size = $groupSize;
×
NEW
1018
            if ($i < $remain) {
×
NEW
1019
                ++$size;
×
1020
            }
NEW
1021
            if ($size) {
×
NEW
1022
                $groups->push(new static(array_slice($this->items, $start, $size)));
×
NEW
1023
                $start += $size;
×
1024
            }
1025
        }
NEW
1026
        return $groups;
×
1027
    }
1028

1029
    /**
1030
     * Chunk the underlying collection array.
1031
     *
1032
     * @return static<int, static<TKey, TValue>>
1033
     */
NEW
1034
    public function chunk(int $size): static
×
1035
    {
NEW
1036
        if ($size <= 0) {
×
NEW
1037
            return new static();
×
1038
        }
NEW
1039
        $chunks = [];
×
NEW
1040
        foreach (array_chunk($this->items, $size, true) as $chunk) {
×
NEW
1041
            $chunks[] = new static($chunk);
×
1042
        }
NEW
1043
        return new static($chunks);
×
1044
    }
1045

1046
    /**
1047
     * Sort through each item with a callback.
1048
     *
1049
     * @param callable(TValue, TValue): int $callback
1050
     * @return static<TKey, TValue>
1051
     */
NEW
1052
    public function sort(?callable $callback = null): static
×
1053
    {
NEW
1054
        $items = $this->items;
×
NEW
1055
        $callback ? uasort($items, $callback) : asort($items);
×
NEW
1056
        return new static($items);
×
1057
    }
1058

1059
    /**
1060
     * Sort the collection using the given callback.
1061
     *
1062
     * @param array|(callable(TValue, TKey): mixed)|string $callback
1063
     * @return static<TKey, TValue>
1064
     */
NEW
1065
    public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): static
×
1066
    {
NEW
1067
        if (is_array($callback) && ! is_callable($callback)) {
×
NEW
1068
            return $this->sortByMany($callback, $options);
×
1069
        }
1070

NEW
1071
        $results = [];
×
NEW
1072
        $callback = $this->valueRetriever($callback);
×
1073
        // First we will loop through the items and get the comparator from a callback
1074
        // function which we were given. Then, we will sort the returned values and
1075
        // and grab the corresponding values for the sorted keys from this array.
NEW
1076
        foreach ($this->items as $key => $value) {
×
NEW
1077
            $results[$key] = $callback($value, $key);
×
1078
        }
NEW
1079
        $descending ? arsort($results, $options) : asort($results, $options);
×
1080
        // Once we have sorted all of the keys in the array, we will loop through them
1081
        // and grab the corresponding model so we can set the underlying items list
1082
        // to the sorted version. Then we'll just return the collection instance.
NEW
1083
        foreach (array_keys($results) as $key) {
×
NEW
1084
            $results[$key] = $this->items[$key];
×
1085
        }
NEW
1086
        return new static($results);
×
1087
    }
1088

1089
    /**
1090
     * Sort the collection in descending order using the given callback.
1091
     *
1092
     * @param array|(callable(TValue, TKey): mixed)|string $callback
1093
     * @return static<TKey, TValue>
1094
     */
NEW
1095
    public function sortByDesc($callback, int $options = SORT_REGULAR): static
×
1096
    {
NEW
1097
        if (is_array($callback) && ! is_callable($callback)) {
×
NEW
1098
            foreach ($callback as $index => $key) {
×
NEW
1099
                $comparison = Arr::wrap($key);
×
1100

NEW
1101
                $comparison[1] = 'desc';
×
1102

NEW
1103
                $callback[$index] = $comparison;
×
1104
            }
1105
        }
1106

NEW
1107
        return $this->sortBy($callback, $options, true);
×
1108
    }
1109

1110
    /**
1111
     * Sort items in descending order.
1112
     *
1113
     * @return static<TKey, TValue>
1114
     */
NEW
1115
    public function sortDesc(int $options = SORT_REGULAR): static
×
1116
    {
NEW
1117
        $items = $this->items;
×
1118

NEW
1119
        arsort($items, $options);
×
1120

NEW
1121
        return new static($items);
×
1122
    }
1123

1124
    /**
1125
     * Sort the collection keys.
1126
     *
1127
     * @return static<TKey, TValue>
1128
     */
NEW
1129
    public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): static
×
1130
    {
NEW
1131
        $items = $this->items;
×
NEW
1132
        $descending ? krsort($items, $options) : ksort($items, $options);
×
NEW
1133
        return new static($items);
×
1134
    }
1135

1136
    /**
1137
     * Sort the collection keys in descending order.
1138
     *
1139
     * @return static<TKey, TValue>
1140
     */
NEW
1141
    public function sortKeysDesc(int $options = SORT_REGULAR): static
×
1142
    {
NEW
1143
        return $this->sortKeys($options, true);
×
1144
    }
1145

1146
    /**
1147
     * Sort the collection keys using a callback.
1148
     *
1149
     * @param callable(TKey, TKey): int $callback
1150
     * @return static<TKey, TValue>
1151
     */
NEW
1152
    public function sortKeysUsing(callable $callback): static
×
1153
    {
NEW
1154
        $items = $this->items;
×
1155

NEW
1156
        uksort($items, $callback);
×
1157

NEW
1158
        return new static($items);
×
1159
    }
1160

1161
    /**
1162
     * Splice a portion of the underlying collection array.
1163
     *
1164
     * @param array<array-key, TValue> $replacement
1165
     * @return static<TKey, TValue>
1166
     */
NEW
1167
    public function splice(int $offset, ?int $length = null, $replacement = []): static
×
1168
    {
NEW
1169
        if (func_num_args() === 1) {
×
NEW
1170
            return new static(array_splice($this->items, $offset));
×
1171
        }
NEW
1172
        return new static(array_splice($this->items, $offset, $length, $replacement));
×
1173
    }
1174

1175
    /**
1176
     * Split a collection into a certain number of groups, and fill the first groups completely.
1177
     *
1178
     * @return static<int, static<TKey, TValue>>
1179
     */
NEW
1180
    public function splitIn(int $numberOfGroups)
×
1181
    {
NEW
1182
        return $this->chunk((int) ceil($this->count() / $numberOfGroups));
×
1183
    }
1184

1185
    /**
1186
     * Take the first or last {$limit} items.
1187
     *
1188
     * @return static<TKey, TValue>
1189
     */
NEW
1190
    public function take(int $limit): static
×
1191
    {
NEW
1192
        if ($limit < 0) {
×
NEW
1193
            return $this->slice($limit, abs($limit));
×
1194
        }
NEW
1195
        return $this->slice(0, $limit);
×
1196
    }
1197

1198
    /**
1199
     * Transform each item in the collection using a callback.
1200
     *
1201
     * @param callable(TValue, TKey): TValue $callback
1202
     * @return $this
1203
     */
NEW
1204
    public function transform(callable $callback): static
×
1205
    {
NEW
1206
        $this->items = $this->map($callback)->all();
×
NEW
1207
        return $this;
×
1208
    }
1209

1210
    /**
1211
     * Prepend one or more items to the beginning of the collection.
1212
     *
1213
     * @param TValue ...$values
1214
     * @return $this
1215
     */
NEW
1216
    public function unshift(...$values)
×
1217
    {
NEW
1218
        array_unshift($this->items, ...$values);
×
1219

NEW
1220
        return $this;
×
1221
    }
1222

1223
    /**
1224
     * Reset the keys on the underlying array.
1225
     *
1226
     * @return static<TKey, TValue>
1227
     */
NEW
1228
    public function values(): static
×
1229
    {
NEW
1230
        return new static(array_values($this->items));
×
1231
    }
1232

1233
    /**
1234
     * Zip the collection together with one or more arrays.
1235
     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1236
     *      => [[1, 4], [2, 5], [3, 6]].
1237
     *
1238
     * @template TZipValue
1239
     *
1240
     * @param Arrayable<array-key, TZipValue>|iterable<array-key, TZipValue> ...$items
1241
     * @return static<int, static<int, TValue|TZipValue>>
1242
     */
NEW
1243
    public function zip($items): Enumerable
×
1244
    {
NEW
1245
        $arrayableItems = array_map(function ($items) {
×
NEW
1246
            return $this->getArrayableItems($items);
×
NEW
1247
        }, func_get_args());
×
NEW
1248
        $params = array_merge([
×
NEW
1249
            function () {
×
NEW
1250
                return new static(func_get_args());
×
NEW
1251
            },
×
NEW
1252
            $this->items,
×
NEW
1253
        ], $arrayableItems);
×
NEW
1254
        return new static(call_user_func_array('array_map', $params));
×
1255
    }
1256

1257
    /**
1258
     * Pad collection to the specified length with a value.
1259
     *
1260
     * @template TPadValue
1261
     *
1262
     * @param TPadValue $value
1263
     * @return static<int, TPadValue|TValue>
1264
     */
NEW
1265
    public function pad(int $size, $value): Enumerable
×
1266
    {
NEW
1267
        return new static(array_pad($this->items, $size, $value));
×
1268
    }
1269

1270
    /**
1271
     * Retrieve duplicate items from the collection.
1272
     *
1273
     * @param null|(callable(TValue): bool)|string $callback
1274
     * @param bool $strict
1275
     * @return static
1276
     */
NEW
1277
    public function duplicates($callback = null, $strict = false)
×
1278
    {
NEW
1279
        $items = $this->map($this->valueRetriever($callback));
×
1280

NEW
1281
        $uniqueItems = $items->unique(null, $strict);
×
1282

NEW
1283
        $compare = $this->duplicateComparator($strict);
×
1284

NEW
1285
        $duplicates = new static();
×
1286

NEW
1287
        foreach ($items as $key => $value) {
×
NEW
1288
            if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
×
NEW
1289
                $uniqueItems->shift();
×
1290
            } else {
NEW
1291
                $duplicates[$key] = $value;
×
1292
            }
1293
        }
1294

NEW
1295
        return $duplicates;
×
1296
    }
1297

1298
    /**
1299
     * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
1300
     *
1301
     * @param (callable(TValue, TKey): bool)|string $key
1302
     * @param mixed $operator
1303
     * @param mixed $value
1304
     * @return TValue
1305
     *
1306
     * @throws ItemNotFoundException
1307
     * @throws MultipleItemsFoundException
1308
     */
NEW
1309
    public function sole($key = null, $operator = null, $value = null)
×
1310
    {
NEW
1311
        $filter = func_num_args() > 1
×
NEW
1312
            ? $this->operatorForWhere(...func_get_args())
×
NEW
1313
            : $key;
×
1314

NEW
1315
        $items = $this->unless($filter == null)->filter($filter);
×
1316

NEW
1317
        $count = $items->count();
×
1318

NEW
1319
        if ($count === 0) {
×
NEW
1320
            throw new ItemNotFoundException();
×
1321
        }
1322

NEW
1323
        if ($count > 1) {
×
NEW
1324
            throw new MultipleItemsFoundException($count);
×
1325
        }
1326

NEW
1327
        return $items->first();
×
1328
    }
1329

1330
    /**
1331
     * Get an iterator for the items.
1332
     *
1333
     * @return ArrayIterator<TKey, TValue>
1334
     */
NEW
1335
    public function getIterator(): ArrayIterator
×
1336
    {
NEW
1337
        return new ArrayIterator($this->items);
×
1338
    }
1339

1340
    /**
1341
     * Count the number of items in the collection.
1342
     */
NEW
1343
    public function count(): int
×
1344
    {
NEW
1345
        return count($this->items);
×
1346
    }
1347

1348
    /**
1349
     * Get a base Support collection instance from this collection.
1350
     *
1351
     * @return Collection<TKey, TValue>
1352
     */
NEW
1353
    public function toBase()
×
1354
    {
NEW
1355
        return new self($this);
×
1356
    }
1357

1358
    /**
1359
     * Determine if an item exists at an offset.
1360
     *
1361
     * @param TKey $offset
1362
     */
NEW
1363
    public function offsetExists(mixed $offset): bool
×
1364
    {
NEW
1365
        return isset($this->items[$offset]);
×
1366
    }
1367

1368
    /**
1369
     * Get an item at a given offset.
1370
     *
1371
     * @param TKey $offset
1372
     * @return TValue
1373
     */
NEW
1374
    public function offsetGet(mixed $offset): mixed
×
1375
    {
NEW
1376
        return $this->items[$offset];
×
1377
    }
1378

1379
    /**
1380
     * Set the item at a given offset.
1381
     *
1382
     * @param null|TKey $offset
1383
     * @param TValue $value
1384
     */
NEW
1385
    public function offsetSet(mixed $offset, mixed $value): void
×
1386
    {
NEW
1387
        if (is_null($offset)) {
×
NEW
1388
            $this->items[] = $value;
×
1389
        } else {
NEW
1390
            $this->items[$offset] = $value;
×
1391
        }
1392
    }
1393

1394
    /**
1395
     * Unset the item at a given offset.
1396
     *
1397
     * @param TKey $offset
1398
     */
NEW
1399
    public function offsetUnset(mixed $offset): void
×
1400
    {
NEW
1401
        unset($this->items[$offset]);
×
1402
    }
1403

1404
    /**
1405
     * Get the first item in the collection but throw an exception if no matching items exist.
1406
     *
1407
     * @param (callable(TValue, TKey): bool)|string $key
1408
     * @param mixed $operator
1409
     * @param mixed $value
1410
     * @return TValue
1411
     *
1412
     * @throws ItemNotFoundException
1413
     */
NEW
1414
    public function firstOrFail($key = null, $operator = null, $value = null)
×
1415
    {
NEW
1416
        $filter = func_num_args() > 1
×
NEW
1417
            ? $this->operatorForWhere(...func_get_args())
×
NEW
1418
            : $key;
×
1419

NEW
1420
        $placeholder = new stdClass();
×
1421

NEW
1422
        $item = $this->first($filter, $placeholder);
×
1423

NEW
1424
        if ($item === $placeholder) {
×
NEW
1425
            throw new ItemNotFoundException();
×
1426
        }
1427

NEW
1428
        return $item;
×
1429
    }
1430

1431
    /**
1432
     * Join all items from the collection using a string. The final items can use a separate glue string.
1433
     *
1434
     * @param string $glue
1435
     * @param string $finalGlue
1436
     * @return string
1437
     */
NEW
1438
    public function join($glue, $finalGlue = '')
×
1439
    {
NEW
1440
        if ($finalGlue === '') {
×
NEW
1441
            return $this->implode($glue);
×
1442
        }
1443

NEW
1444
        $count = $this->count();
×
1445

NEW
1446
        if ($count === 0) {
×
NEW
1447
            return '';
×
1448
        }
1449

NEW
1450
        if ($count === 1) {
×
NEW
1451
            return $this->last();
×
1452
        }
1453

NEW
1454
        $collection = new static($this->items);
×
1455

NEW
1456
        $finalItem = $collection->pop();
×
1457

NEW
1458
        return $collection->implode($glue) . $finalGlue . $finalItem;
×
1459
    }
1460

1461
    /**
1462
     * Intersect the collection with the given items with additional index check, using the callback.
1463
     *
1464
     * @param Arrayable<array-key, TValue>|iterable<array-key, TValue> $items
1465
     * @param callable(TValue, TValue): int $callback
1466
     * @return static
1467
     */
NEW
1468
    public function intersectAssocUsing($items, callable $callback)
×
1469
    {
NEW
1470
        return new static(array_intersect_uassoc($this->items, $this->getArrayableItems($items), $callback));
×
1471
    }
1472

1473
    /**
1474
     * Intersect the collection with the given items, using the callback.
1475
     *
1476
     * @param Arrayable<array-key, TValue>|iterable<array-key, TValue> $items
1477
     * @param callable(TValue, TValue): int $callback
1478
     * @return static
1479
     */
NEW
1480
    public function intersectUsing($items, callable $callback)
×
1481
    {
NEW
1482
        return new static(array_uintersect($this->items, $this->getArrayableItems($items), $callback));
×
1483
    }
1484

1485
    /**
1486
     * Retrieve duplicate items from the collection using strict comparison.
1487
     *
1488
     * @param null|(callable(TValue): bool)|string $callback
1489
     * @return static
1490
     */
NEW
1491
    public function duplicatesStrict($callback = null)
×
1492
    {
NEW
1493
        return $this->duplicates($callback, true);
×
1494
    }
1495

1496
    /**
1497
     * Get a lazy collection for the items in this collection.
1498
     *
1499
     * @return LazyCollection<TKey, TValue>
1500
     */
NEW
1501
    public function lazy()
×
1502
    {
NEW
1503
        return new LazyCollection($this->items);
×
1504
    }
1505

1506
    /**
1507
     * Skip items in the collection until the given condition is met.
1508
     *
1509
     * @param callable(TValue,TKey): bool|TValue $value
1510
     * @return static
1511
     */
NEW
1512
    public function skipUntil($value)
×
1513
    {
NEW
1514
        return new static($this->lazy()->skipUntil($value)->all());
×
1515
    }
1516

1517
    /**
1518
     * Skip items in the collection while the given condition is met.
1519
     *
1520
     * @param callable(TValue,TKey): bool|TValue $value
1521
     * @return static
1522
     */
NEW
1523
    public function skipWhile($value)
×
1524
    {
NEW
1525
        return new static($this->lazy()->skipWhile($value)->all());
×
1526
    }
1527

1528
    /**
1529
     * Chunk the collection into chunks with a callback.
1530
     *
1531
     * @param callable(TValue, TKey, static<int, TValue>): bool $callback
1532
     * @return static<int, static<int, TValue>>
1533
     */
NEW
1534
    public function chunkWhile(callable $callback)
×
1535
    {
NEW
1536
        return new static(
×
NEW
1537
            $this->lazy()->chunkWhile($callback)->mapInto(static::class)
×
NEW
1538
        );
×
1539
    }
1540

1541
    /**
1542
     * Take items in the collection until the given condition is met.
1543
     *
1544
     * @param callable(TValue,TKey): bool|TValue $value
1545
     * @return static
1546
     */
NEW
1547
    public function takeUntil($value)
×
1548
    {
NEW
1549
        return new static($this->lazy()->takeUntil($value)->all());
×
1550
    }
1551

1552
    /**
1553
     * Take items in the collection while the given condition is met.
1554
     *
1555
     * @param callable(TValue,TKey): bool|TValue $value
1556
     * @return static
1557
     */
NEW
1558
    public function takeWhile($value)
×
1559
    {
NEW
1560
        return new static($this->lazy()->takeWhile($value)->all());
×
1561
    }
1562

1563
    /**
1564
     * Count the number of items in the collection by a field or using a callback.
1565
     *
1566
     * @param null|(callable(TValue, TKey): array-key)|string $countBy
1567
     * @return static<array-key, int>
1568
     */
NEW
1569
    public function countBy($countBy = null)
×
1570
    {
NEW
1571
        return new static($this->lazy()->countBy($countBy)->all());
×
1572
    }
1573

1574
    /**
1575
     * Sort the collection using multiple comparisons.
1576
     *
1577
     * @return static
1578
     */
NEW
1579
    protected function sortByMany(array $comparisons = [], int $options = SORT_REGULAR)
×
1580
    {
NEW
1581
        $items = $this->items;
×
1582

NEW
1583
        uasort($items, function ($a, $b) use ($comparisons, $options) {
×
NEW
1584
            foreach ($comparisons as $comparison) {
×
NEW
1585
                $comparison = Arr::wrap($comparison);
×
1586

NEW
1587
                $prop = $comparison[0];
×
1588

NEW
1589
                $ascending = Arr::get($comparison, 1, true) === true
×
NEW
1590
                    || Arr::get($comparison, 1, true) === 'asc';
×
1591

NEW
1592
                if (! is_string($prop) && is_callable($prop)) {
×
NEW
1593
                    $result = $prop($a, $b);
×
1594
                } else {
NEW
1595
                    $values = [data_get($a, $prop), data_get($b, $prop)];
×
1596

NEW
1597
                    if (! $ascending) {
×
NEW
1598
                        $values = array_reverse($values);
×
1599
                    }
1600

NEW
1601
                    if (($options & SORT_FLAG_CASE) === SORT_FLAG_CASE) {
×
NEW
1602
                        if (($options & SORT_NATURAL) === SORT_NATURAL) {
×
NEW
1603
                            $result = strnatcasecmp($values[0], $values[1]);
×
1604
                        } else {
NEW
1605
                            $result = strcasecmp($values[0], $values[1]);
×
1606
                        }
1607
                    } else {
NEW
1608
                        $result = match ($options) {
×
NEW
1609
                            SORT_NUMERIC => intval($values[0]) <=> intval($values[1]),
×
NEW
1610
                            SORT_STRING => strcmp($values[0], $values[1]),
×
NEW
1611
                            SORT_NATURAL => strnatcmp((string) $values[0], (string) $values[1]),
×
NEW
1612
                            SORT_LOCALE_STRING => strcoll($values[0], $values[1]),
×
NEW
1613
                            default => $values[0] <=> $values[1],
×
NEW
1614
                        };
×
1615
                    }
1616
                }
1617

NEW
1618
                if ($result === 0) {
×
NEW
1619
                    continue;
×
1620
                }
1621

NEW
1622
                return $result;
×
1623
            }
NEW
1624
        });
×
1625

1626
        // TODO: The code will be removed in v3.2
NEW
1627
        if (array_is_list($this->items)) {
×
NEW
1628
            $items = array_values($items);
×
1629
        }
1630

NEW
1631
        return new static($items);
×
1632
    }
1633

1634
    /**
1635
     * Get the comparison function to detect duplicates.
1636
     *
1637
     * @param bool $strict
1638
     * @return callable(TValue, TValue): bool
1639
     */
NEW
1640
    protected function duplicateComparator($strict)
×
1641
    {
NEW
1642
        if ($strict) {
×
NEW
1643
            return fn ($a, $b) => $a === $b;
×
1644
        }
1645

NEW
1646
        return fn ($a, $b) => $a == $b;
×
1647
    }
1648

1649
    /**
1650
     * Results array of items from Collection or Arrayable.
1651
     * @param null|Arrayable<TKey,TValue>|iterable<TKey,TValue>|Jsonable|JsonSerializable|static<TKey,TValue> $items
1652
     * @return array<TKey,TValue>
1653
     */
NEW
1654
    protected function getArrayableItems($items): array
×
1655
    {
NEW
1656
        return match (true) {
×
NEW
1657
            is_array($items) => $items,
×
NEW
1658
            $items instanceof self => $items->all(),
×
NEW
1659
            $items instanceof Arrayable => $items->toArray(),
×
NEW
1660
            $items instanceof Jsonable => json_decode($items->__toString(), true),
×
NEW
1661
            $items instanceof JsonSerializable => $items->jsonSerialize(),
×
NEW
1662
            $items instanceof Traversable => iterator_to_array($items),
×
NEW
1663
            default => (array) $items,
×
NEW
1664
        };
×
1665
    }
1666
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc