Collection   F
last analyzed

Complexity

Total Complexity 159

Size/Duplication

Total Lines 1390
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 289
c 3
b 0
f 0
dl 0
loc 1390
rs 2
wmc 159

89 Methods

Rating   Name   Duplication   Size   Complexity  
A sortKeysDesc() 0 3 1
A median() 0 22 4
A countBy() 0 3 1
A sortDesc() 0 7 1
A skip() 0 3 1
A diffKeysUsing() 0 3 1
A contains() 0 13 3
A offsetUnset() 0 3 1
A splice() 0 7 2
A zip() 0 11 1
A pull() 0 3 1
A duplicatesStrict() 0 3 1
A takeUntil() 0 3 1
A mapToDictionary() 0 19 3
A getIterator() 0 3 1
A all() 0 3 1
A forget() 0 7 2
A push() 0 7 2
A sortByDesc() 0 3 1
A collapse() 0 3 1
A shuffle() 0 3 1
A pop() 0 3 1
A except() 0 9 3
A diffAssoc() 0 3 1
A crossJoin() 0 4 1
A reduce() 0 3 1
A replaceRecursive() 0 3 1
A merge() 0 3 1
A reverse() 0 3 1
A offsetSet() 0 6 2
A mapWithKeys() 0 13 3
A sortKeys() 0 7 2
A diffAssocUsing() 0 3 1
A avg() 0 12 2
A flatten() 0 3 1
A slice() 0 3 1
A keyBy() 0 17 3
A nth() 0 15 3
A times() 0 11 3
A mergeRecursive() 0 3 1
A concat() 0 9 2
A toBase() 0 3 1
A only() 0 13 4
A replace() 0 3 1
A flip() 0 3 1
A transform() 0 5 1
A combine() 0 3 1
A diffKeys() 0 3 1
A get() 0 7 2
A duplicates() 0 19 4
A diff() 0 3 1
A sortBy() 0 30 5
A join() 0 21 4
A take() 0 7 2
A intersect() 0 3 1
A first() 0 3 1
A isEmpty() 0 3 1
A pluck() 0 3 1
A map() 0 7 1
A prepend() 0 5 1
A count() 0 3 1
A skipUntil() 0 3 1
A keys() 0 3 1
A union() 0 3 1
A offsetExists() 0 3 1
A pad() 0 3 1
A implode() 0 9 3
A lazy() 0 3 1
A diffUsing() 0 3 1
A sort() 0 9 3
A skipWhile() 0 3 1
A __construct() 0 3 1
A has() 0 11 4
A put() 0 5 1
A shift() 0 3 1
A last() 0 3 1
A offsetGet() 0 3 1
A intersectByKeys() 0 4 1
A chunk() 0 13 3
A split() 0 29 5
A add() 0 5 1
A random() 0 7 2
A search() 0 13 4
B groupBy() 0 37 10
A values() 0 3 1
A filter() 0 7 2
A mode() 0 21 4
A takeWhile() 0 3 1
A duplicateComparator() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like Collection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Collection, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace MuCTS\Collections;
4
5
use ArrayAccess;
6
use ArrayIterator;
7
use Closure;
8
use InvalidArgumentException;
9
use MuCTS\Collections\Traits\EnumeratesValues;
10
use MuCTS\Contracts\Collections\Enumerable;
11
use MuCTS\Contracts\Collections\Collection as CollectionContract;
12
use MuCTS\Macroable\Traits\Macroable;
13
use stdClass;
14
15
class Collection implements ArrayAccess, Enumerable, CollectionContract
16
{
17
    use EnumeratesValues, Macroable;
0 ignored issues
show
Bug introduced by
The trait MuCTS\Macroable\Traits\Macroable requires the property $name which is not provided by MuCTS\Collections\Collection.
Loading history...
18
19
    /** @var array 排序规则 */
20
    protected array $multiSort = [];
21
22
    /**
23
     * The items contained in the collection.
24
     *
25
     * @var array
26
     */
27
    protected array $items = [];
28
29
    /**
30
     * Create a new collection.
31
     *
32
     * @param mixed $items
33
     * @return void
34
     */
35
    public function __construct($items = [])
36
    {
37
        $this->items = $this->getArrayableItems($items);
38
    }
39
40
    /**
41
     * Create a new collection by invoking the callback a given amount of times.
42
     *
43
     * @param int $number
44
     * @param callable|null $callback
45
     * @return static
46
     */
47
    public static function times($number, callable $callback = null)
48
    {
49
        if ($number < 1) {
50
            return new static;
51
        }
52
53
        if (is_null($callback)) {
54
            return new static(range(1, $number));
55
        }
56
57
        return (new static(range(1, $number)))->map($callback);
58
    }
59
60
    /**
61
     * Get all of the items in the collection.
62
     *
63
     * @return array
64
     */
65
    public function all()
66
    {
67
        return $this->items;
68
    }
69
70
    /**
71
     * Get a lazy collection for the items in this collection.
72
     *
73
     * @return LazyCollection
74
     */
75
    public function lazy()
76
    {
77
        return new LazyCollection($this->items);
78
    }
79
80
    /**
81
     * Get the average value of a given key.
82
     *
83
     * @param callable|string|null $callback
84
     * @return mixed
85
     */
86
    public function avg($callback = null)
87
    {
88
        $callback = $this->valueRetriever($callback);
89
90
        $items = $this->map(function ($value) use ($callback) {
91
            return $callback($value);
92
        })->filter(function ($value) {
93
            return !is_null($value);
94
        });
95
96
        if ($count = $items->count()) {
97
            return $items->sum() / $count;
98
        }
99
    }
100
101
    /**
102
     * Get the median of a given key.
103
     *
104
     * @param string|array|null $key
105
     * @return mixed
106
     */
107
    public function median($key = null)
108
    {
109
        $values = (isset($key) ? $this->pluck($key) : $this)
110
            ->filter(function ($item) {
111
                return !is_null($item);
112
            })->sort()->values();
113
114
        $count = $values->count();
115
116
        if ($count === 0) {
117
            return null;
118
        }
119
120
        $middle = (int)($count / 2);
121
122
        if ($count % 2) {
123
            return $values->get($middle);
124
        }
125
126
        return (new static([
127
            $values->get($middle - 1), $values->get($middle),
128
        ]))->average();
129
    }
130
131
    /**
132
     * Get the mode of a given key.
133
     *
134
     * @param string|array|null $key
135
     * @return array|null
136
     */
137
    public function mode($key = null)
138
    {
139
        if ($this->count() === 0) {
140
            return null;
141
        }
142
143
        $collection = isset($key) ? $this->pluck($key) : $this;
144
145
        $counts = new static;
146
147
        $collection->each(function ($value) use ($counts) {
148
            $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
149
        });
150
151
        $sorted = $counts->sort();
152
153
        $highestValue = $sorted->last();
154
155
        return $sorted->filter(function ($value) use ($highestValue) {
156
            return $value == $highestValue;
157
        })->sort()->keys()->all();
158
    }
159
160
    /**
161
     * Collapse the collection of items into a single array.
162
     *
163
     * @return static
164
     */
165
    public function collapse()
166
    {
167
        return new static(Arr::collapse($this->items));
168
    }
169
170
    /**
171
     * Determine if an item exists in the collection.
172
     *
173
     * @param mixed $key
174
     * @param mixed $operator
175
     * @param mixed $value
176
     * @return bool
177
     */
178
    public function contains($key, $operator = null, $value = null)
179
    {
180
        if (func_num_args() === 1) {
181
            if ($this->useAsCallable($key)) {
182
                $placeholder = new stdClass;
183
184
                return $this->first($key, $placeholder) !== $placeholder;
185
            }
186
187
            return in_array($key, $this->items);
188
        }
189
190
        return $this->contains($this->operatorForWhere(...func_get_args()));
0 ignored issues
show
Bug introduced by
func_get_args() is expanded, but the parameter $key of MuCTS\Collections\Collection::operatorForWhere() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
        return $this->contains($this->operatorForWhere(/** @scrutinizer ignore-type */ ...func_get_args()));
Loading history...
191
    }
192
193
    /**
194
     * Cross join with the given lists, returning all possible permutations.
195
     *
196
     * @param mixed ...$lists
197
     * @return static
198
     */
199
    public function crossJoin(...$lists)
200
    {
201
        return new static(Arr::crossJoin(
202
            $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
203
        ));
204
    }
205
206
    /**
207
     * Get the items in the collection that are not present in the given items.
208
     *
209
     * @param mixed $items
210
     * @return static
211
     */
212
    public function diff($items)
213
    {
214
        return new static(array_diff($this->items, $this->getArrayableItems($items)));
215
    }
216
217
    /**
218
     * Get the items in the collection that are not present in the given items, using the callback.
219
     *
220
     * @param mixed $items
221
     * @param callable $callback
222
     * @return static
223
     */
224
    public function diffUsing($items, callable $callback)
225
    {
226
        return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
227
    }
228
229
    /**
230
     * Get the items in the collection whose keys and values are not present in the given items.
231
     *
232
     * @param mixed $items
233
     * @return static
234
     */
235
    public function diffAssoc($items)
236
    {
237
        return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
238
    }
239
240
    /**
241
     * Get the items in the collection whose keys and values are not present in the given items, using the callback.
242
     *
243
     * @param mixed $items
244
     * @param callable $callback
245
     * @return static
246
     */
247
    public function diffAssocUsing($items, callable $callback)
248
    {
249
        return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
250
    }
251
252
    /**
253
     * Get the items in the collection whose keys are not present in the given items.
254
     *
255
     * @param mixed $items
256
     * @return static
257
     */
258
    public function diffKeys($items)
259
    {
260
        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
261
    }
262
263
    /**
264
     * Get the items in the collection whose keys are not present in the given items, using the callback.
265
     *
266
     * @param mixed $items
267
     * @param callable $callback
268
     * @return static
269
     */
270
    public function diffKeysUsing($items, callable $callback)
271
    {
272
        return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
273
    }
274
275
    /**
276
     * Retrieve duplicate items from the collection.
277
     *
278
     * @param callable|null $callback
279
     * @param bool $strict
280
     * @return static
281
     */
282
    public function duplicates($callback = null, $strict = false)
283
    {
284
        $items = $this->map($this->valueRetriever($callback));
285
286
        $uniqueItems = $items->unique(null, $strict);
287
288
        $compare = $this->duplicateComparator($strict);
289
290
        $duplicates = new static;
291
292
        foreach ($items as $key => $value) {
293
            if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
294
                $uniqueItems->shift();
295
            } else {
296
                $duplicates[$key] = $value;
297
            }
298
        }
299
300
        return $duplicates;
301
    }
302
303
    /**
304
     * Retrieve duplicate items from the collection using strict comparison.
305
     *
306
     * @param callable|null $callback
307
     * @return static
308
     */
309
    public function duplicatesStrict($callback = null)
310
    {
311
        return $this->duplicates($callback, true);
312
    }
313
314
    /**
315
     * Get the comparison function to detect duplicates.
316
     *
317
     * @param bool $strict
318
     * @return Closure
319
     */
320
    protected function duplicateComparator($strict)
321
    {
322
        if ($strict) {
323
            return function ($a, $b) {
324
                return $a === $b;
325
            };
326
        }
327
328
        return function ($a, $b) {
329
            return $a == $b;
330
        };
331
    }
332
333
    /**
334
     * Get all items except for those with the specified keys.
335
     *
336
     * @param Collection|mixed $keys
337
     * @return static
338
     */
339
    public function except($keys)
340
    {
341
        if ($keys instanceof Enumerable) {
342
            $keys = $keys->all();
343
        } elseif (!is_array($keys)) {
344
            $keys = func_get_args();
345
        }
346
347
        return new static(Arr::except($this->items, $keys));
348
    }
349
350
    /**
351
     * Run a filter over each of the items.
352
     *
353
     * @param callable|null $callback
354
     * @return static
355
     */
356
    public function filter(callable $callback = null)
357
    {
358
        if ($callback) {
359
            return new static(Arr::where($this->items, $callback));
360
        }
361
362
        return new static(array_filter($this->items));
363
    }
364
365
    /**
366
     * Get the first item from the collection passing the given truth test.
367
     *
368
     * @param callable|null $callback
369
     * @param mixed $default
370
     * @return mixed
371
     */
372
    public function first(callable $callback = null, $default = null)
373
    {
374
        return Arr::first($this->items, $callback, $default);
375
    }
376
377
    /**
378
     * Get a flattened array of the items in the collection.
379
     *
380
     * @param int $depth
381
     * @return static
382
     */
383
    public function flatten($depth = INF)
384
    {
385
        return new static(Arr::flatten($this->items, $depth));
0 ignored issues
show
Bug introduced by
It seems like $depth can also be of type double; however, parameter $depth of MuCTS\Collections\Arr::flatten() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

385
        return new static(Arr::flatten($this->items, /** @scrutinizer ignore-type */ $depth));
Loading history...
386
    }
387
388
    /**
389
     * Flip the items in the collection.
390
     *
391
     * @return static
392
     */
393
    public function flip()
394
    {
395
        return new static(array_flip($this->items));
396
    }
397
398
    /**
399
     * Remove an item from the collection by key.
400
     *
401
     * @param string|array $keys
402
     * @return $this
403
     */
404
    public function forget($keys)
405
    {
406
        foreach ((array)$keys as $key) {
407
            $this->offsetUnset($key);
408
        }
409
410
        return $this;
411
    }
412
413
    /**
414
     * Get an item from the collection by key.
415
     *
416
     * @param mixed $key
417
     * @param mixed $default
418
     * @return mixed
419
     */
420
    public function get($key, $default = null)
421
    {
422
        if (array_key_exists($key, $this->items)) {
423
            return $this->items[$key];
424
        }
425
426
        return value($default);
427
    }
428
429
    /**
430
     * Group an associative array by a field or using a callback.
431
     *
432
     * @param array|callable|string $groupBy
433
     * @param bool $preserveKeys
434
     * @return static|Collection|HigherOrderCollectionProxy
435
     */
436
    public function groupBy($groupBy, $preserveKeys = false)
437
    {
438
        if (!$this->useAsCallable($groupBy) && is_array($groupBy)) {
439
            $nextGroups = $groupBy;
440
441
            $groupBy = array_shift($nextGroups);
442
        }
443
444
        $groupBy = $this->valueRetriever($groupBy);
445
446
        $results = [];
447
448
        foreach ($this->items as $key => $value) {
449
            $groupKeys = $groupBy($value, $key);
450
451
            if (!is_array($groupKeys)) {
452
                $groupKeys = [$groupKeys];
453
            }
454
455
            foreach ($groupKeys as $groupKey) {
456
                $groupKey = is_bool($groupKey) ? (int)$groupKey : $groupKey;
457
458
                if (!array_key_exists($groupKey, $results)) {
459
                    $results[$groupKey] = new static;
460
                }
461
462
                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
463
            }
464
        }
465
466
        $result = new static($results);
467
468
        if (!empty($nextGroups)) {
469
            return $result->map->groupBy($nextGroups, $preserveKeys);
0 ignored issues
show
Bug introduced by
The method groupBy() does not exist on MuCTS\Collections\HigherOrderCollectionProxy. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

469
            return $result->map->/** @scrutinizer ignore-call */ groupBy($nextGroups, $preserveKeys);
Loading history...
470
        }
471
472
        return $result;
473
    }
474
475
    /**
476
     * Key an associative array by a field or using a callback.
477
     *
478
     * @param callable|string $keyBy
479
     * @return static
480
     */
481
    public function keyBy($keyBy)
482
    {
483
        $keyBy = $this->valueRetriever($keyBy);
484
485
        $results = [];
486
487
        foreach ($this->items as $key => $item) {
488
            $resolvedKey = $keyBy($item, $key);
489
490
            if (is_object($resolvedKey)) {
491
                $resolvedKey = (string)$resolvedKey;
492
            }
493
494
            $results[$resolvedKey] = $item;
495
        }
496
497
        return new static($results);
498
    }
499
500
    /**
501
     * Determine if an item exists in the collection by key.
502
     *
503
     * @param mixed $key
504
     * @return bool
505
     */
506
    public function has($key)
507
    {
508
        $keys = is_array($key) ? $key : func_get_args();
509
510
        foreach ($keys as $value) {
511
            if (!array_key_exists($value, $this->items)) {
512
                return false;
513
            }
514
        }
515
516
        return true;
517
    }
518
519
    /**
520
     * Concatenate values of a given key as a string.
521
     *
522
     * @param string $value
523
     * @param string|null $glue
524
     * @return string
525
     */
526
    public function implode($value, $glue = null)
527
    {
528
        $first = $this->first();
529
530
        if (is_array($first) || is_object($first)) {
531
            return implode($glue, $this->pluck($value)->all());
0 ignored issues
show
Bug introduced by
It seems like $glue can also be of type null; however, parameter $glue of implode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

531
            return implode(/** @scrutinizer ignore-type */ $glue, $this->pluck($value)->all());
Loading history...
532
        }
533
534
        return implode($value, $this->items);
535
    }
536
537
    /**
538
     * Intersect the collection with the given items.
539
     *
540
     * @param mixed $items
541
     * @return static
542
     */
543
    public function intersect($items)
544
    {
545
        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
546
    }
547
548
    /**
549
     * Intersect the collection with the given items by key.
550
     *
551
     * @param mixed $items
552
     * @return static
553
     */
554
    public function intersectByKeys($items)
555
    {
556
        return new static(array_intersect_key(
557
            $this->items, $this->getArrayableItems($items)
558
        ));
559
    }
560
561
    /**
562
     * Determine if the collection is empty or not.
563
     *
564
     * @return bool
565
     */
566
    public function isEmpty()
567
    {
568
        return empty($this->items);
569
    }
570
571
    /**
572
     * Join all items from the collection using a string. The final items can use a separate glue string.
573
     *
574
     * @param string $glue
575
     * @param string $finalGlue
576
     * @return string
577
     */
578
    public function join($glue, $finalGlue = '')
579
    {
580
        if ($finalGlue === '') {
581
            return $this->implode($glue);
582
        }
583
584
        $count = $this->count();
585
586
        if ($count === 0) {
587
            return '';
588
        }
589
590
        if ($count === 1) {
591
            return $this->last();
592
        }
593
594
        $collection = new static($this->items);
595
596
        $finalItem = $collection->pop();
597
598
        return $collection->implode($glue) . $finalGlue . $finalItem;
599
    }
600
601
    /**
602
     * Get the keys of the collection items.
603
     *
604
     * @return static
605
     */
606
    public function keys()
607
    {
608
        return new static(array_keys($this->items));
609
    }
610
611
    /**
612
     * Get the last item from the collection.
613
     *
614
     * @param callable|null $callback
615
     * @param mixed $default
616
     * @return mixed
617
     */
618
    public function last(callable $callback = null, $default = null)
619
    {
620
        return Arr::last($this->items, $callback, $default);
621
    }
622
623
    /**
624
     * Get the values of a given key.
625
     *
626
     * @param string|array $value
627
     * @param string|null $key
628
     * @return static
629
     */
630
    public function pluck($value, $key = null)
631
    {
632
        return new static(Arr::pluck($this->items, $value, $key));
633
    }
634
635
    /**
636
     * Run a map over each of the items.
637
     *
638
     * @param callable $callback
639
     * @return static
640
     */
641
    public function map(callable $callback)
642
    {
643
        $keys = array_keys($this->items);
644
645
        $items = array_map($callback, $this->items, $keys);
646
647
        return new static(array_combine($keys, $items));
648
    }
649
650
    /**
651
     * Run a dictionary map over the items.
652
     *
653
     * The callback should return an associative array with a single key/value pair.
654
     *
655
     * @param callable $callback
656
     * @return static
657
     */
658
    public function mapToDictionary(callable $callback)
659
    {
660
        $dictionary = [];
661
662
        foreach ($this->items as $key => $item) {
663
            $pair = $callback($item, $key);
664
665
            $key = key($pair);
666
667
            $value = reset($pair);
668
669
            if (!isset($dictionary[$key])) {
670
                $dictionary[$key] = [];
671
            }
672
673
            $dictionary[$key][] = $value;
674
        }
675
676
        return new static($dictionary);
677
    }
678
679
    /**
680
     * Run an associative map over each of the items.
681
     *
682
     * The callback should return an associative array with a single key/value pair.
683
     *
684
     * @param callable $callback
685
     * @return static
686
     */
687
    public function mapWithKeys(callable $callback)
688
    {
689
        $result = [];
690
691
        foreach ($this->items as $key => $value) {
692
            $assoc = $callback($value, $key);
693
694
            foreach ($assoc as $mapKey => $mapValue) {
695
                $result[$mapKey] = $mapValue;
696
            }
697
        }
698
699
        return new static($result);
700
    }
701
702
    /**
703
     * Merge the collection with the given items.
704
     *
705
     * @param mixed $items
706
     * @return static
707
     */
708
    public function merge($items)
709
    {
710
        return new static(array_merge($this->items, $this->getArrayableItems($items)));
711
    }
712
713
    /**
714
     * Recursively merge the collection with the given items.
715
     *
716
     * @param mixed $items
717
     * @return static
718
     */
719
    public function mergeRecursive($items)
720
    {
721
        return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
722
    }
723
724
    /**
725
     * Create a collection by using this collection for keys and another for its values.
726
     *
727
     * @param mixed $values
728
     * @return static
729
     */
730
    public function combine($values)
731
    {
732
        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
733
    }
734
735
    /**
736
     * Union the collection with the given items.
737
     *
738
     * @param mixed $items
739
     * @return static
740
     */
741
    public function union($items)
742
    {
743
        return new static($this->items + $this->getArrayableItems($items));
744
    }
745
746
    /**
747
     * Create a new collection consisting of every n-th element.
748
     *
749
     * @param int $step
750
     * @param int $offset
751
     * @return static
752
     */
753
    public function nth($step, $offset = 0)
754
    {
755
        $new = [];
756
757
        $position = 0;
758
759
        foreach ($this->items as $item) {
760
            if ($position % $step === $offset) {
761
                $new[] = $item;
762
            }
763
764
            $position++;
765
        }
766
767
        return new static($new);
768
    }
769
770
    /**
771
     * Get the items with the specified keys.
772
     *
773
     * @param mixed $keys
774
     * @return static
775
     */
776
    public function only($keys)
777
    {
778
        if (is_null($keys)) {
779
            return new static($this->items);
780
        }
781
782
        if ($keys instanceof Enumerable) {
783
            $keys = $keys->all();
784
        }
785
786
        $keys = is_array($keys) ? $keys : func_get_args();
787
788
        return new static(Arr::only($this->items, $keys));
789
    }
790
791
    /**
792
     * Get and remove the last item from the collection.
793
     *
794
     * @return mixed
795
     */
796
    public function pop()
797
    {
798
        return array_pop($this->items);
799
    }
800
801
    /**
802
     * Push an item onto the beginning of the collection.
803
     *
804
     * @param mixed $value
805
     * @param mixed $key
806
     * @return $this
807
     */
808
    public function prepend($value, $key = null)
809
    {
810
        $this->items = Arr::prepend($this->items, $value, $key);
811
812
        return $this;
813
    }
814
815
    /**
816
     * Push one or more items onto the end of the collection.
817
     *
818
     * @param mixed $values [optional]
819
     * @return $this
820
     */
821
    public function push(...$values)
822
    {
823
        foreach ($values as $value) {
824
            $this->items[] = $value;
825
        }
826
827
        return $this;
828
    }
829
830
    /**
831
     * Push all of the given items onto the collection.
832
     *
833
     * @param iterable $source
834
     * @return static
835
     */
836
    public function concat($source)
837
    {
838
        $result = new static($this);
839
840
        foreach ($source as $item) {
841
            $result->push($item);
842
        }
843
844
        return $result;
845
    }
846
847
    /**
848
     * Get and remove an item from the collection.
849
     *
850
     * @param mixed $key
851
     * @param mixed $default
852
     * @return mixed
853
     */
854
    public function pull($key, $default = null)
855
    {
856
        return Arr::pull($this->items, $key, $default);
857
    }
858
859
    /**
860
     * Put an item in the collection by key.
861
     *
862
     * @param mixed $key
863
     * @param mixed $value
864
     * @return $this
865
     */
866
    public function put($key, $value)
867
    {
868
        $this->offsetSet($key, $value);
869
870
        return $this;
871
    }
872
873
    /**
874
     * Get one or a specified number of items randomly from the collection.
875
     *
876
     * @param int|null $number
877
     * @return static|mixed
878
     *
879
     * @throws InvalidArgumentException
880
     */
881
    public function random($number = null)
882
    {
883
        if (is_null($number)) {
884
            return Arr::random($this->items);
885
        }
886
887
        return new static(Arr::random($this->items, $number));
888
    }
889
890
    /**
891
     * Reduce the collection to a single value.
892
     *
893
     * @param callable $callback
894
     * @param mixed $initial
895
     * @return mixed
896
     */
897
    public function reduce(callable $callback, $initial = null)
898
    {
899
        return array_reduce($this->items, $callback, $initial);
900
    }
901
902
    /**
903
     * Replace the collection items with the given items.
904
     *
905
     * @param mixed $items
906
     * @return static
907
     */
908
    public function replace($items)
909
    {
910
        return new static(array_replace($this->items, $this->getArrayableItems($items)));
911
    }
912
913
    /**
914
     * Recursively replace the collection items with the given items.
915
     *
916
     * @param mixed $items
917
     * @return static
918
     */
919
    public function replaceRecursive($items)
920
    {
921
        return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
922
    }
923
924
    /**
925
     * Reverse items order.
926
     *
927
     * @return static
928
     */
929
    public function reverse()
930
    {
931
        return new static(array_reverse($this->items, true));
932
    }
933
934
    /**
935
     * Search the collection for a given value and return the corresponding key if successful.
936
     *
937
     * @param mixed $value
938
     * @param bool $strict
939
     * @return mixed
940
     */
941
    public function search($value, $strict = false)
942
    {
943
        if (!$this->useAsCallable($value)) {
944
            return array_search($value, $this->items, $strict);
945
        }
946
947
        foreach ($this->items as $key => $item) {
948
            if ($value($item, $key)) {
949
                return $key;
950
            }
951
        }
952
953
        return false;
954
    }
955
956
    /**
957
     * Get and remove the first item from the collection.
958
     *
959
     * @return mixed
960
     */
961
    public function shift()
962
    {
963
        return array_shift($this->items);
964
    }
965
966
    /**
967
     * Shuffle the items in the collection.
968
     *
969
     * @param int|null $seed
970
     * @return static
971
     */
972
    public function shuffle($seed = null)
973
    {
974
        return new static(Arr::shuffle($this->items, $seed));
975
    }
976
977
    /**
978
     * Skip the first {$count} items.
979
     *
980
     * @param int $count
981
     * @return static
982
     */
983
    public function skip($count)
984
    {
985
        return $this->slice($count);
986
    }
987
988
    /**
989
     * Skip items in the collection until the given condition is met.
990
     *
991
     * @param mixed $value
992
     * @return static
993
     */
994
    public function skipUntil($value)
995
    {
996
        return new static($this->lazy()->skipUntil($value)->all());
997
    }
998
999
    /**
1000
     * Skip items in the collection while the given condition is met.
1001
     *
1002
     * @param mixed $value
1003
     * @return static
1004
     */
1005
    public function skipWhile($value)
1006
    {
1007
        return new static($this->lazy()->skipWhile($value)->all());
1008
    }
1009
1010
    /**
1011
     * Slice the underlying collection array.
1012
     *
1013
     * @param int $offset
1014
     * @param int|null $length
1015
     * @return static
1016
     */
1017
    public function slice($offset, $length = null)
1018
    {
1019
        return new static(array_slice($this->items, $offset, $length, true));
1020
    }
1021
1022
    /**
1023
     * Split a collection into a certain number of groups.
1024
     *
1025
     * @param int $numberOfGroups
1026
     * @return static
1027
     */
1028
    public function split($numberOfGroups)
1029
    {
1030
        if ($this->isEmpty()) {
1031
            return new static;
1032
        }
1033
1034
        $groups = new static;
1035
1036
        $groupSize = floor($this->count() / $numberOfGroups);
1037
1038
        $remain = $this->count() % $numberOfGroups;
1039
1040
        $start = 0;
1041
1042
        for ($i = 0; $i < $numberOfGroups; $i++) {
1043
            $size = $groupSize;
1044
1045
            if ($i < $remain) {
1046
                $size++;
1047
            }
1048
1049
            if ($size) {
1050
                $groups->push(new static(array_slice($this->items, $start, $size)));
0 ignored issues
show
Bug introduced by
$size of type double is incompatible with the type integer expected by parameter $length of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1050
                $groups->push(new static(array_slice($this->items, $start, /** @scrutinizer ignore-type */ $size)));
Loading history...
1051
1052
                $start += $size;
1053
            }
1054
        }
1055
1056
        return $groups;
1057
    }
1058
1059
    /**
1060
     * Chunk the collection into chunks of the given size.
1061
     *
1062
     * @param int $size
1063
     * @return static
1064
     */
1065
    public function chunk($size)
1066
    {
1067
        if ($size <= 0) {
1068
            return new static;
1069
        }
1070
1071
        $chunks = [];
1072
1073
        foreach (array_chunk($this->items, $size, true) as $chunk) {
1074
            $chunks[] = new static($chunk);
1075
        }
1076
1077
        return new static($chunks);
1078
    }
1079
1080
    /**
1081
     * Sort through each item with a callback.
1082
     *
1083
     * @param callable|int|null $callback
1084
     * @return static
1085
     */
1086
    public function sort($callback = null)
1087
    {
1088
        $items = $this->items;
1089
1090
        $callback && is_callable($callback)
1091
            ? uasort($items, $callback)
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type integer; however, parameter $cmp_function of uasort() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1091
            ? uasort($items, /** @scrutinizer ignore-type */ $callback)
Loading history...
1092
            : asort($items, $callback);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type callable; however, parameter $sort_flags of asort() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1092
            : asort($items, /** @scrutinizer ignore-type */ $callback);
Loading history...
1093
1094
        return new static($items);
1095
    }
1096
1097
    /**
1098
     * Sort items in descending order.
1099
     *
1100
     * @param int $options
1101
     * @return static
1102
     */
1103
    public function sortDesc($options = SORT_REGULAR)
1104
    {
1105
        $items = $this->items;
1106
1107
        arsort($items, $options);
1108
1109
        return new static($items);
1110
    }
1111
1112
    /**
1113
     * Sort the collection using the given callback.
1114
     *
1115
     * @param callable|string $callback
1116
     * @param bool $sorting
1117
     * @param int $options
1118
     * @param bool $descending
1119
     * @return static
1120
     */
1121
    public function sortBy($callback, $sorting = true, $options = SORT_REGULAR, $descending = false)
1122
    {
1123
        $results = [];
1124
1125
        $callback = $this->valueRetriever($callback);
1126
1127
        // First we will loop through the items and get the comparator from a callback
1128
        // function which we were given. Then, we will sort the returned values and
1129
        // and grab the corresponding values for the sorted keys from this array.
1130
        foreach ($this->items as $key => $value) {
1131
            $results[$key] = $callback($value, $key);
1132
        }
1133
1134
        array_push($this->multiSort, $results, $descending ? SORT_DESC : SORT_ASC, $options);
1135
        if (!$sorting) {
1136
            return $this;
1137
        }
1138
        $keys = array_keys($results);
1139
        $this->multiSort[] = &$keys;
1140
        call_user_func_array('array_multisort', $this->multiSort);
1141
        $collects = new static();
1142
1143
        // Once we have sorted all of the keys in the array, we will loop through them
1144
        // and grab the corresponding model so we can set the underlying items list
1145
        // to the sorted version. Then we'll just return the collection instance.
1146
        foreach ($keys as $key) {
1147
            $collects->push($this->items[$key]);
1148
        }
1149
1150
        return $collects;
1151
    }
1152
1153
    /**
1154
     * Sort the collection in descending order using the given callback.
1155
     *
1156
     * @param callable|string $callback
1157
     * @param bool $sorting
1158
     * @param int $options
1159
     * @return static
1160
     */
1161
    public function sortByDesc($callback, $sorting = true, $options = SORT_REGULAR)
1162
    {
1163
        return $this->sortBy($callback, $sorting, $options, true);
1164
    }
1165
1166
    /**
1167
     * Sort the collection keys.
1168
     *
1169
     * @param int $options
1170
     * @param bool $descending
1171
     * @return static
1172
     */
1173
    public function sortKeys($options = SORT_REGULAR, $descending = false)
1174
    {
1175
        $items = $this->items;
1176
1177
        $descending ? krsort($items, $options) : ksort($items, $options);
1178
1179
        return new static($items);
1180
    }
1181
1182
    /**
1183
     * Sort the collection keys in descending order.
1184
     *
1185
     * @param int $options
1186
     * @return static
1187
     */
1188
    public function sortKeysDesc($options = SORT_REGULAR)
1189
    {
1190
        return $this->sortKeys($options, true);
1191
    }
1192
1193
    /**
1194
     * Splice a portion of the underlying collection array.
1195
     *
1196
     * @param int $offset
1197
     * @param int|null $length
1198
     * @param mixed $replacement
1199
     * @return static
1200
     */
1201
    public function splice($offset, $length = null, $replacement = [])
1202
    {
1203
        if (func_num_args() === 1) {
1204
            return new static(array_splice($this->items, $offset));
1205
        }
1206
1207
        return new static(array_splice($this->items, $offset, $length, $replacement));
1208
    }
1209
1210
    /**
1211
     * Take the first or last {$limit} items.
1212
     *
1213
     * @param int $limit
1214
     * @return static
1215
     */
1216
    public function take($limit)
1217
    {
1218
        if ($limit < 0) {
1219
            return $this->slice($limit, abs($limit));
0 ignored issues
show
Bug introduced by
It seems like abs($limit) can also be of type double; however, parameter $length of MuCTS\Collections\Collection::slice() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1219
            return $this->slice($limit, /** @scrutinizer ignore-type */ abs($limit));
Loading history...
1220
        }
1221
1222
        return $this->slice(0, $limit);
1223
    }
1224
1225
    /**
1226
     * Take items in the collection until the given condition is met.
1227
     *
1228
     * @param mixed $value
1229
     * @return static
1230
     */
1231
    public function takeUntil($value)
1232
    {
1233
        return new static($this->lazy()->takeUntil($value)->all());
1234
    }
1235
1236
    /**
1237
     * Take items in the collection while the given condition is met.
1238
     *
1239
     * @param mixed $value
1240
     * @return static
1241
     */
1242
    public function takeWhile($value)
1243
    {
1244
        return new static($this->lazy()->takeWhile($value)->all());
1245
    }
1246
1247
    /**
1248
     * Transform each item in the collection using a callback.
1249
     *
1250
     * @param callable $callback
1251
     * @return $this
1252
     */
1253
    public function transform(callable $callback)
1254
    {
1255
        $this->items = $this->map($callback)->all();
1256
1257
        return $this;
1258
    }
1259
1260
    /**
1261
     * Reset the keys on the underlying array.
1262
     *
1263
     * @return static
1264
     */
1265
    public function values()
1266
    {
1267
        return new static(array_values($this->items));
1268
    }
1269
1270
    /**
1271
     * Zip the collection together with one or more arrays.
1272
     *
1273
     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1274
     *      => [[1, 4], [2, 5], [3, 6]]
1275
     *
1276
     * @param mixed ...$items
1277
     * @return static
1278
     */
1279
    public function zip($items)
1280
    {
1281
        $arrayableItems = array_map(function ($items) {
1282
            return $this->getArrayableItems($items);
1283
        }, func_get_args());
1284
1285
        $params = array_merge([function () {
1286
            return new static(func_get_args());
1287
        }, $this->items], $arrayableItems);
1288
1289
        return new static(call_user_func_array('array_map', $params));
1290
    }
1291
1292
    /**
1293
     * Pad collection to the specified length with a value.
1294
     *
1295
     * @param int $size
1296
     * @param mixed $value
1297
     * @return static
1298
     */
1299
    public function pad($size, $value)
1300
    {
1301
        return new static(array_pad($this->items, $size, $value));
1302
    }
1303
1304
    /**
1305
     * Get an iterator for the items.
1306
     *
1307
     * @return ArrayIterator
1308
     */
1309
    public function getIterator()
1310
    {
1311
        return new ArrayIterator($this->items);
1312
    }
1313
1314
    /**
1315
     * Count the number of items in the collection.
1316
     *
1317
     * @return int
1318
     */
1319
    public function count()
1320
    {
1321
        return count($this->items);
1322
    }
1323
1324
    /**
1325
     * Count the number of items in the collection by a field or using a callback.
1326
     *
1327
     * @param callable|string $countBy
1328
     * @return static
1329
     */
1330
    public function countBy($countBy = null)
1331
    {
1332
        return new static($this->lazy()->countBy($countBy)->all());
1333
    }
1334
1335
    /**
1336
     * Add an item to the collection.
1337
     *
1338
     * @param mixed $item
1339
     * @return $this
1340
     */
1341
    public function add($item)
1342
    {
1343
        $this->items[] = $item;
1344
1345
        return $this;
1346
    }
1347
1348
    /**
1349
     * Get a base Support collection instance from this collection.
1350
     *
1351
     * @return Collection
1352
     */
1353
    public function toBase()
1354
    {
1355
        return new self($this);
1356
    }
1357
1358
    /**
1359
     * Determine if an item exists at an offset.
1360
     *
1361
     * @param mixed $key
1362
     * @return bool
1363
     */
1364
    public function offsetExists($key)
1365
    {
1366
        return isset($this->items[$key]);
1367
    }
1368
1369
    /**
1370
     * Get an item at a given offset.
1371
     *
1372
     * @param mixed $key
1373
     * @return mixed
1374
     */
1375
    public function offsetGet($key)
1376
    {
1377
        return $this->items[$key];
1378
    }
1379
1380
    /**
1381
     * Set the item at a given offset.
1382
     *
1383
     * @param mixed $key
1384
     * @param mixed $value
1385
     * @return void
1386
     */
1387
    public function offsetSet($key, $value)
1388
    {
1389
        if (is_null($key)) {
1390
            $this->items[] = $value;
1391
        } else {
1392
            $this->items[$key] = $value;
1393
        }
1394
    }
1395
1396
    /**
1397
     * Unset the item at a given offset.
1398
     *
1399
     * @param string $key
1400
     * @return void
1401
     */
1402
    public function offsetUnset($key)
1403
    {
1404
        unset($this->items[$key]);
1405
    }
1406
}
1407