Completed
Push — master ( e1255d...678b0e )
by Antonio Carlos
02:36 queued 59s
created

Collection::diffUsing()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace IlluminateAgnostic\Str\Support;
4
5
use stdClass;
6
use Countable;
7
use Exception;
8
use ArrayAccess;
9
use Traversable;
10
use ArrayIterator;
11
use CachingIterator;
12
use JsonSerializable;
13
use IteratorAggregate;
14
use IlluminateAgnostic\Str\Support\Traits\Macroable;
15
use IlluminateAgnostic\Str\Contracts\Support\Jsonable;
16
use Symfony\Component\VarDumper\VarDumper;
17
use IlluminateAgnostic\Str\Contracts\Support\Arrayable;
18
19
/**
20
 * @property-read HigherOrderCollectionProxy $average
21
 * @property-read HigherOrderCollectionProxy $avg
22
 * @property-read HigherOrderCollectionProxy $contains
23
 * @property-read HigherOrderCollectionProxy $each
24
 * @property-read HigherOrderCollectionProxy $every
25
 * @property-read HigherOrderCollectionProxy $filter
26
 * @property-read HigherOrderCollectionProxy $first
27
 * @property-read HigherOrderCollectionProxy $flatMap
28
 * @property-read HigherOrderCollectionProxy $groupBy
29
 * @property-read HigherOrderCollectionProxy $keyBy
30
 * @property-read HigherOrderCollectionProxy $map
31
 * @property-read HigherOrderCollectionProxy $max
32
 * @property-read HigherOrderCollectionProxy $min
33
 * @property-read HigherOrderCollectionProxy $partition
34
 * @property-read HigherOrderCollectionProxy $reject
35
 * @property-read HigherOrderCollectionProxy $sortBy
36
 * @property-read HigherOrderCollectionProxy $sortByDesc
37
 * @property-read HigherOrderCollectionProxy $sum
38
 * @property-read HigherOrderCollectionProxy $unique
39
 */
40
class Collection implements ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
41
{
42
    use Macroable;
43
44
    /**
45
     * The items contained in the collection.
46
     *
47
     * @var array
48
     */
49
    protected $items = [];
50
51
    /**
52
     * The methods that can be proxied.
53
     *
54
     * @var array
55
     */
56
    protected static $proxies = [
57
        'average', 'avg', 'contains', 'each', 'every', 'filter', 'first',
58
        'flatMap', 'groupBy', 'keyBy', 'map', 'max', 'min', 'partition',
59
        'reject', 'some', 'sortBy', 'sortByDesc', 'sum', 'unique',
60
    ];
61
62
    /**
63
     * Create a new collection.
64
     *
65
     * @param  mixed  $items
66
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
67
     */
68
    public function __construct($items = [])
69
    {
70 238
        $this->items = $this->getArrayableItems($items);
71
    }
72 238
73 238
    /**
74
     * Create a new collection instance if the value isn't one already.
75
     *
76
     * @param  mixed  $items
77
     * @return static
78
     */
79
    public static function make($items = [])
80
    {
81 10
        return new static($items);
82
    }
83 10
84
    /**
85
     * Wrap the given value in a collection if applicable.
86
     *
87
     * @param  mixed  $value
88
     * @return static
89
     */
90
    public static function wrap($value)
91
    {
92 7
        return $value instanceof self
93
            ? new static($value)
94 7
            : new static(Arr::wrap($value));
95 2
    }
96 7
97
    /**
98
     * Get the underlying items from the given collection if applicable.
99
     *
100
     * @param  array|static  $value
101
     * @return array
102
     */
103
    public static function unwrap($value)
104
    {
105 3
        return $value instanceof self ? $value->all() : $value;
106
    }
107 3
108
    /**
109
     * Create a new collection by invoking the callback a given amount of times.
110
     *
111
     * @param  int  $number
112
     * @param  callable  $callback
113
     * @return static
114
     */
115
    public static function times($number, callable $callback = null)
116
    {
117 1
        if ($number < 1) {
118
            return new static;
119 1
        }
120 1
121
        if (is_null($callback)) {
122
            return new static(range(1, $number));
123 1
        }
124 1
125
        return (new static(range(1, $number)))->map($callback);
126
    }
127 1
128
    /**
129
     * Get all of the items in the collection.
130
     *
131
     * @return array
132
     */
133
    public function all()
134
    {
135 116
        return $this->items;
136
    }
137 116
138
    /**
139
     * Get the average value of a given key.
140
     *
141
     * @param  callable|string|null  $callback
142
     * @return mixed
143
     */
144
    public function avg($callback = null)
145
    {
146 4
        $callback = $this->valueRetriever($callback);
147
148 4
        $items = $this->map(function ($value) use ($callback) {
149
            return $callback($value);
150
        })->filter(function ($value) {
151 4
            return ! is_null($value);
152
        });
153 4
154 4
        if ($count = $items->count()) {
155
            return $items->sum() / $count;
156 4
        }
157 4
    }
158
159 1
    /**
160
     * Alias for the "avg" method.
161
     *
162
     * @param  callable|string|null  $callback
163
     * @return mixed
164
     */
165
    public function average($callback = null)
166
    {
167 3
        return $this->avg($callback);
168
    }
169 3
170
    /**
171
     * Get the median of a given key.
172
     *
173
     * @param  string|array|null $key
174
     * @return mixed
175
     */
176
    public function median($key = null)
177
    {
178 6
        $values = (isset($key) ? $this->pluck($key) : $this)
179
            ->filter(function ($item) {
180 6
                return ! is_null($item);
181
            })->sort()->values();
182 5
183 6
        $count = $values->count();
184
185 6
        if ($count === 0) {
186
            return;
187 6
        }
188 1
189
        $middle = (int) ($count / 2);
190
191 5
        if ($count % 2) {
192
            return $values->get($middle);
193 5
        }
194 2
195
        return (new static([
196
            $values->get($middle - 1), $values->get($middle),
197 3
        ]))->average();
198 3
    }
199 3
200
    /**
201
     * Get the mode of a given key.
202
     *
203
     * @param  string|array|null  $key
204
     * @return array|null
205
     */
206
    public function mode($key = null)
207
    {
208 4
        if ($this->count() === 0) {
209
            return;
210 4
        }
211 1
212
        $collection = isset($key) ? $this->pluck($key) : $this;
213
214 3
        $counts = new self;
215
216 3
        $collection->each(function ($value) use ($counts) {
217
            $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
218
        });
219 3
220 3
        $sorted = $counts->sort();
221
222 3
        $highestValue = $sorted->last();
223
224 3
        return $sorted->filter(function ($value) use ($highestValue) {
225
            return $value == $highestValue;
226
        })->sort()->keys()->all();
227 3
    }
228 3
229
    /**
230
     * Collapse the collection of items into a single array.
231
     *
232
     * @return static
233
     */
234
    public function collapse()
235
    {
236 3
        return new static(Arr::collapse($this->items));
237
    }
238 3
239
    /**
240
     * Alias for the "contains" method.
241
     *
242
     * @param  mixed  $key
243
     * @param  mixed  $operator
244
     * @param  mixed  $value
245
     * @return bool
246
     */
247
    public function some($key, $operator = null, $value = null)
248
    {
249 1
        return $this->contains(...func_get_args());
250
    }
251 1
252
    /**
253
     * Determine if an item exists in the collection.
254
     *
255
     * @param  mixed  $key
256
     * @param  mixed  $operator
257
     * @param  mixed  $value
258
     * @return bool
259
     */
260
    public function contains($key, $operator = null, $value = null)
261
    {
262 4
        if (func_num_args() === 1) {
263
            if ($this->useAsCallable($key)) {
264 4
                $placeholder = new stdClass;
265 4
266 4
                return $this->first($key, $placeholder) !== $placeholder;
267
            }
268 4
269
            return in_array($key, $this->items);
270
        }
271 2
272
        return $this->contains($this->operatorForWhere(...func_get_args()));
273
    }
274 3
275
    /**
276
     * Determine if an item exists in the collection using strict comparison.
277
     *
278
     * @param  mixed  $key
279
     * @param  mixed  $value
280
     * @return bool
281
     */
282
    public function containsStrict($key, $value = null)
283
    {
284 1
        if (func_num_args() === 2) {
285
            return $this->contains(function ($item) use ($key, $value) {
286 1
                return data_get($item, $key) === $value;
287
            });
288 1
        }
289 1
290
        if ($this->useAsCallable($key)) {
291
            return ! is_null($this->first($key));
292 1
        }
293 1
294
        return in_array($key, $this->items, true);
295
    }
296 1
297
    /**
298
     * Cross join with the given lists, returning all possible permutations.
299
     *
300
     * @param  mixed  ...$lists
301
     * @return static
302
     */
303
    public function crossJoin(...$lists)
304
    {
305 1
        return new static(Arr::crossJoin(
306
            $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
307 1
        ));
308 1
    }
309
310
    /**
311
     * Dump the collection and end the script.
312
     *
313
     * @param  mixed  ...$args
314
     * @return void
315
     */
316
    public function dd(...$args)
317
    {
318
        call_user_func_array([$this, 'dump'], $args);
319
320
        die(1);
321
    }
322
323
    /**
324
     * Dump the collection.
325
     *
326
     * @return $this
327
     */
328
    public function dump()
329
    {
330
        (new static(func_get_args()))
331
            ->push($this)
332
            ->each(function ($item) {
333
                VarDumper::dump($item);
334
            });
335
336
        return $this;
337
    }
338
339
    /**
340
     * Get the items in the collection that are not present in the given items.
341
     *
342
     * @param  mixed  $items
343
     * @return static
344
     */
345
    public function diff($items)
346
    {
347 3
        return new static(array_diff($this->items, $this->getArrayableItems($items)));
348
    }
349 3
350
    /**
351
     * Get the items in the collection that are not present in the given items.
352
     *
353
     * @param  mixed  $items
354
     * @param  callable  $callback
355
     * @return static
356
     */
357
    public function diffUsing($items, callable $callback)
358
    {
359 2
        return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
360
    }
361 2
362
    /**
363
     * Get the items in the collection whose keys and values are not present in the given items.
364
     *
365
     * @param  mixed  $items
366
     * @return static
367
     */
368
    public function diffAssoc($items)
369
    {
370 2
        return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
371
    }
372 2
373
    /**
374
     * Get the items in the collection whose keys and values are not present in the given items.
375
     *
376
     * @param  mixed  $items
377
     * @param  callable  $callback
378
     * @return static
379
     */
380
    public function diffAssocUsing($items, callable $callback)
381
    {
382 1
        return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
383
    }
384 1
385
    /**
386
     * Get the items in the collection whose keys are not present in the given items.
387
     *
388
     * @param  mixed  $items
389
     * @return static
390
     */
391
    public function diffKeys($items)
392
    {
393 2
        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
394
    }
395 2
396
    /**
397
     * Get the items in the collection whose keys are not present in the given items.
398
     *
399
     * @param  mixed   $items
400
     * @param  callable  $callback
401
     * @return static
402
     */
403
    public function diffKeysUsing($items, callable $callback)
404
    {
405 1
        return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
406
    }
407 1
408
    /**
409
     * Retrieve duplicate items from the collection.
410
     *
411
     * @param  callable|null  $callback
412
     * @param  bool  $strict
413
     * @return static
414
     */
415
    public function duplicates($callback = null, $strict = false)
416 7
    {
417
        $items = $this->map($this->valueRetriever($callback));
418 7
419 7
        $uniqueItems = $items->unique(null, $strict);
420 2
421
        $compare = $this->duplicateComparator($strict);
422
423
        $duplicates = new static;
424 7
425
        foreach ($items as $key => $value) {
426
            if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
427
                $uniqueItems->shift();
428
            } else {
429
                $duplicates[$key] = $value;
430
            }
431
        }
432
433 1
        return $duplicates;
434
    }
435
436 1
    /**
437
     * Retrieve duplicate items from the collection using strict comparison.
438 1
     *
439 1
     * @param  callable|null  $callback
440
     * @return static
441
     */
442
    public function duplicatesStrict($callback = null)
443
    {
444
        return $this->duplicates($callback, true);
445
    }
446
447
    /**
448
     * Get the comparison function to detect duplicates.
449
     *
450 1
     * @param  bool  $strict
451
     * @return \Closure
452 1
     */
453 1
    protected function duplicateComparator($strict)
454
    {
455 1
        if ($strict) {
456 1
            return function ($a, $b) {
457 1
                return $a === $b;
458
            };
459
        }
460
461 1
        return function ($a, $b) {
462
            return $a == $b;
463
        };
464 1
    }
465
466
    /**
467
     * Execute a callback over each item.
468
     *
469
     * @param  callable  $callback
470
     * @return $this
471
     */
472
    public function each(callable $callback)
473 2
    {
474
        foreach ($this->items as $key => $item) {
475 2
            if ($callback($item, $key) === false) {
476 2
                break;
477 1
            }
478 1
        }
479
480
        return $this;
481 2
    }
482
483
    /**
484
     * Execute a callback over each nested chunk of items.
485
     *
486
     * @param  callable  $callback
487
     * @return static
488
     */
489
    public function eachSpread(callable $callback)
490 32
    {
491
        return $this->each(function ($chunk, $key) use ($callback) {
492 32
            $chunk[] = $key;
493 32
494
            return $callback(...$chunk);
495
        });
496 1
    }
497
498
    /**
499
     * Determine if all items in the collection pass the given test.
500
     *
501
     * @param  string|callable  $key
502
     * @param  mixed  $operator
503
     * @param  mixed  $value
504
     * @return bool
505
     */
506
    public function every($key, $operator = null, $value = null)
507 12
    {
508
        if (func_num_args() === 1) {
509 12
            $callback = $this->valueRetriever($key);
510 8
511 10
            foreach ($this->items as $k => $v) {
512 4
                if (! $callback($v, $k)) {
513
                    return false;
514
                }
515 6
            }
516
517
            return true;
518
        }
519
520
        return $this->every($this->operatorForWhere(...func_get_args()));
521
    }
522
523
    /**
524
     * Get all items except for those with the specified keys.
525 4
     *
526
     * @param  \IlluminateAgnostic\Str\Support\Collection|mixed  $keys
527 4
     * @return static
528
     */
529
    public function except($keys)
530
    {
531
        if ($keys instanceof self) {
532
            $keys = $keys->all();
533
        } elseif (! is_array($keys)) {
534
            $keys = func_get_args();
535
        }
536
537 4
        return new static(Arr::except($this->items, $keys));
538
    }
539 4
540
    /**
541
     * Run a filter over each of the items.
542
     *
543
     * @param  callable|null  $callback
544
     * @return static
545
     */
546
    public function filter(callable $callback = null)
547
    {
548
        if ($callback) {
549
            return new static(Arr::where($this->items, $callback));
550 2
        }
551
552 2
        return new static(array_filter($this->items));
553
    }
554
555
    /**
556
     * Apply the callback if the value is truthy.
557
     *
558
     * @param  bool  $value
559
     * @param  callable  $callback
560
     * @param  callable  $default
561
     * @return static|mixed
562 2
     */
563
    public function when($value, callable $callback, callable $default = null)
564 2
    {
565
        if ($value) {
566
            return $callback($this, $value);
567
        } elseif ($default) {
568
            return $default($this, $value);
569
        }
570
571
        return $this;
572
    }
573
574 2
    /**
575
     * Apply the callback if the collection is empty.
576 2
     *
577
     * @param  callable  $callback
578
     * @param  callable  $default
579
     * @return static|mixed
580
     */
581
    public function whenEmpty(callable $callback, callable $default = null)
582
    {
583
        return $this->when($this->isEmpty(), $callback, $default);
584
    }
585
586
    /**
587 3
     * Apply the callback if the collection is not empty.
588
     *
589 3
     * @param  callable  $callback
590
     * @param  callable  $default
591
     * @return static|mixed
592
     */
593
    public function whenNotEmpty(callable $callback, callable $default = null)
594
    {
595
        return $this->when($this->isNotEmpty(), $callback, $default);
596
    }
597
598
    /**
599
     * Apply the callback if the value is falsy.
600 9
     *
601
     * @param  bool  $value
602 9
     * @param  callable  $callback
603 1
     * @param  callable  $default
604
     * @return static|mixed
605 1
     */
606
    public function unless($value, callable $callback, callable $default = null)
607
    {
608 9
        return $this->when(! $value, $callback, $default);
609 6
    }
610
611 6
    /**
612
     * Apply the callback unless the collection is empty.
613
     *
614
     * @param  callable  $callback
615 9
     * @param  callable  $default
616
     * @return static|mixed
617
     */
618 9
    public function unlessEmpty(callable $callback, callable $default = null)
619 9
    {
620
        return $this->whenNotEmpty($callback, $default);
621 9
    }
622 1
623
    /**
624
     * Apply the callback unless the collection is not empty.
625 9
     *
626
     * @param  callable  $callback
627 9
     * @param  callable  $default
628 9
     * @return static|mixed
629 6
     */
630 6
    public function unlessNotEmpty(callable $callback, callable $default = null)
631 6
    {
632 6
        return $this->whenEmpty($callback, $default);
633 6
    }
634 6
635 3
    /**
636 1
     * Filter items by the given key value pair.
637
     *
638 9
     * @param  string  $key
639
     * @param  mixed  $operator
640
     * @param  mixed  $value
641
     * @return static
642
     */
643
    public function where($key, $operator = null, $value = null)
644
    {
645
        return $this->filter($this->operatorForWhere(...func_get_args()));
646
    }
647
648 1
    /**
649
     * Get an operator checker callback.
650 1
     *
651
     * @param  string  $key
652
     * @param  string  $operator
653
     * @param  mixed  $value
654
     * @return \Closure
655
     */
656
    protected function operatorForWhere($key, $operator = null, $value = null)
657
    {
658
        if (func_num_args() === 1) {
659
            $value = true;
660
661 2
            $operator = '=';
662
        }
663 2
664
        if (func_num_args() === 2) {
665
            $value = $operator;
666 2
667 2
            $operator = '=';
668
        }
669
670
        return function ($item) use ($key, $operator, $value) {
671
            $retrieved = data_get($item, $key);
672
673
            $strings = array_filter([$retrieved, $value], function ($value) {
674
                return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
675
            });
676
677 1
            if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
678
                return in_array($operator, ['!=', '<>', '!==']);
679 1
            }
680
681
            switch ($operator) {
682
                default:
683
                case '=':
684
                case '==':  return $retrieved == $value;
685
                case '!=':
686
                case '<>':  return $retrieved != $value;
687
                case '<':   return $retrieved < $value;
688
                case '>':   return $retrieved > $value;
689 1
                case '<=':  return $retrieved <= $value;
690
                case '>=':  return $retrieved >= $value;
691 1
                case '===': return $retrieved === $value;
692
                case '!==': return $retrieved !== $value;
693
            }
694
        };
695
    }
696
697
    /**
698
     * Filter items by the given key value pair using strict comparison.
699
     *
700
     * @param  string  $key
701 1
     * @param  mixed  $value
702
     * @return static
703
     */
704 1
    public function whereStrict($key, $value)
705 1
    {
706
        return $this->where($key, '===', $value);
707
    }
708
709
    /**
710
     * Filter items by the given key value pair.
711
     *
712
     * @param  string  $key
713
     * @param  mixed  $values
714
     * @param  bool  $strict
715
     * @return static
716 2
     */
717 View Code Duplication
    public function whereIn($key, $values, $strict = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
718 2
    {
719
        $values = $this->getArrayableItems($values);
720
721 2
        return $this->filter(function ($item) use ($key, $values, $strict) {
722 2
            return in_array(data_get($item, $key), $values, $strict);
723
        });
724
    }
725
726
    /**
727
     * Filter items by the given key value pair using strict comparison.
728
     *
729
     * @param  string  $key
730
     * @param  mixed  $values
731
     * @return static
732 1
     */
733
    public function whereInStrict($key, $values)
734 1
    {
735
        return $this->whereIn($key, $values, true);
736
    }
737
738
    /**
739
     * Filter items such that the value of the given key is between the given values.
740
     *
741
     * @param  string  $key
742
     * @param  array  $values
743 1
     * @return static
744
     */
745
    public function whereBetween($key, $values)
746 1
    {
747 1
        return $this->where($key, '>=', reset($values))->where($key, '<=', end($values));
748
    }
749
750
    /**
751
     * Filter items such that the value of the given key is not between the given values.
752
     *
753
     * @param  string  $key
754
     * @param  array  $values
755
     * @return static
756
     */
757 13
    public function whereNotBetween($key, $values)
758
    {
759 13
        return $this->filter(function ($item) use ($key, $values) {
760
            return data_get($item, $key) < reset($values) || data_get($item, $key) > end($values);
761
        });
762
    }
763
764
    /**
765
     * Filter items by the given key value pair.
766
     *
767
     * @param  string  $key
768
     * @param  mixed  $values
769
     * @param  bool  $strict
770 1
     * @return static
771
     */
772 1 View Code Duplication
    public function whereNotIn($key, $values, $strict = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
773
    {
774
        $values = $this->getArrayableItems($values);
775
776
        return $this->reject(function ($item) use ($key, $values, $strict) {
777
            return in_array(data_get($item, $key), $values, $strict);
778
        });
779
    }
780
781 3
    /**
782
     * Filter items by the given key value pair using strict comparison.
783 3
     *
784
     * @param  string  $key
785
     * @param  mixed  $values
786
     * @return static
787
     */
788
    public function whereNotInStrict($key, $values)
789
    {
790
        return $this->whereNotIn($key, $values, true);
791 1
    }
792
793 1
    /**
794
     * Filter the items, removing any items that don't match the given type.
795
     *
796
     * @param  string  $type
797
     * @return static
798
     */
799
    public function whereInstanceOf($type)
800
    {
801
        return $this->filter(function ($value) use ($type) {
802 2
            return $value instanceof $type;
803
        });
804 2
    }
805 2
806
    /**
807
     * Get the first item from the collection passing the given truth test.
808 2
     *
809
     * @param  callable|null  $callback
810
     * @param  mixed  $default
811
     * @return mixed
812
     */
813
    public function first(callable $callback = null, $default = null)
814
    {
815
        return Arr::first($this->items, $callback, $default);
816
    }
817
818 6
    /**
819
     * Get the first item by the given key value pair.
820 6
     *
821 5
     * @param  string  $key
822
     * @param  mixed  $operator
823
     * @param  mixed  $value
824 1
     * @return mixed
825
     */
826
    public function firstWhere($key, $operator = null, $value = null)
827
    {
828
        return $this->first($this->operatorForWhere(...func_get_args()));
829
    }
830
831
    /**
832
     * Get a flattened array of the items in the collection.
833
     *
834 10
     * @param  int  $depth
835
     * @return static
836 10
     */
837 1
    public function flatten($depth = INF)
838
    {
839 1
        return new static(Arr::flatten($this->items, $depth));
840
    }
841
842 10
    /**
843
     * Flip the items in the collection.
844 10
     *
845
     * @return static
846 10
     */
847 10
    public function flip()
848
    {
849 10
        return new static(array_flip($this->items));
850 8
    }
851
852
    /**
853 10
     * Remove an item from the collection by key.
854 10
     *
855
     * @param  string|array  $keys
856 10
     * @return $this
857 10
     */
858
    public function forget($keys)
859
    {
860 10
        foreach ((array) $keys as $key) {
861
            $this->offsetUnset($key);
862
        }
863
864 10
        return $this;
865
    }
866 10
867 1
    /**
868
     * Get an item from the collection by key.
869
     *
870 10
     * @param  mixed  $key
871
     * @param  mixed  $default
872
     * @return mixed
873
     */
874
    public function get($key, $default = null)
875
    {
876
        if ($this->offsetExists($key)) {
877
            return $this->items[$key];
878
        }
879 3
880
        return value($default);
881 3
    }
882
883 3
    /**
884
     * Group an associative array by a field or using a callback.
885 3
     *
886 3
     * @param  array|callable|string  $groupBy
887
     * @param  bool  $preserveKeys
888 3
     * @return static
889
     */
890
    public function groupBy($groupBy, $preserveKeys = false)
891
    {
892 3
        if (is_array($groupBy)) {
893
            $nextGroups = $groupBy;
894
895 3
            $groupBy = array_shift($nextGroups);
896
        }
897
898
        $groupBy = $this->valueRetriever($groupBy);
899
900
        $results = [];
901
902
        foreach ($this->items as $key => $value) {
903
            $groupKeys = $groupBy($value, $key);
904 2
905
            if (! is_array($groupKeys)) {
906 2
                $groupKeys = [$groupKeys];
907
            }
908 2
909 2
            foreach ($groupKeys as $groupKey) {
910 2
                $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
911
912
                if (! array_key_exists($groupKey, $results)) {
913
                    $results[$groupKey] = new static;
914 2
                }
915
916
                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
917
            }
918
        }
919
920
        $result = new static($results);
921
922
        if (! empty($nextGroups)) {
923
            return $result->map->groupBy($nextGroups, $preserveKeys);
0 ignored issues
show
Documentation Bug introduced by
The method groupBy does not exist on object<IlluminateAgnosti...erOrderCollectionProxy>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
924 2
        }
925
926 2
        return $result;
927
    }
928 2
929 1
    /**
930
     * Key an associative array by a field or using a callback.
931
     *
932 2
     * @param  callable|string  $keyBy
933
     * @return static
934
     */
935
    public function keyBy($keyBy)
936
    {
937
        $keyBy = $this->valueRetriever($keyBy);
938
939
        $results = [];
940
941 2
        foreach ($this->items as $key => $item) {
942
            $resolvedKey = $keyBy($item, $key);
943 2
944
            if (is_object($resolvedKey)) {
945
                $resolvedKey = (string) $resolvedKey;
946
            }
947
948
            $results[$resolvedKey] = $item;
949
        }
950
951
        return new static($results);
952 2
    }
953
954 2
    /**
955 2
     * Determine if an item exists in the collection by key.
956
     *
957
     * @param  mixed  $key
958
     * @return bool
959
     */
960
    public function has($key)
961
    {
962
        $keys = is_array($key) ? $key : func_get_args();
963
964 19
        foreach ($keys as $value) {
965
            if (! $this->offsetExists($value)) {
966 19
                return false;
967
            }
968
        }
969
970
        return true;
971
    }
972
973
    /**
974 5
     * Concatenate values of a given key as a string.
975
     *
976 5
     * @param  string  $value
977
     * @param  string  $glue
978
     * @return string
979
     */
980
    public function implode($value, $glue = null)
981
    {
982
        $first = $this->first();
983
984
        if (is_array($first) || is_object($first)) {
985 49
            return implode($glue, $this->pluck($value)->all());
986
        }
987 49
988
        return implode($value, $this->items);
989
    }
990
991
    /**
992
     * Intersect the collection with the given items.
993
     *
994
     * @param  mixed  $items
995
     * @return static
996
     */
997 1
    public function intersect($items)
998
    {
999 1
        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
1000 1
    }
1001
1002
    /**
1003 1
     * Intersect the collection with the given items by key.
1004
     *
1005 1
     * @param  mixed  $items
1006 1
     * @return static
1007
     */
1008
    public function intersectByKeys($items)
1009 1
    {
1010 1
        return new static(array_intersect_key(
1011
            $this->items, $this->getArrayableItems($items)
1012
        ));
1013 1
    }
1014
1015 1
    /**
1016
     * Determine if the collection is empty or not.
1017 1
     *
1018
     * @return bool
1019
     */
1020
    public function isEmpty()
1021
    {
1022
        return empty($this->items);
1023
    }
1024
1025 6
    /**
1026
     * Determine if the collection is not empty.
1027 6
     *
1028
     * @return bool
1029
     */
1030
    public function isNotEmpty()
1031
    {
1032
        return ! $this->isEmpty();
1033
    }
1034
1035
    /**
1036
     * Determine if the given value is callable, but not a string.
1037 8
     *
1038
     * @param  mixed  $value
1039 8
     * @return bool
1040
     */
1041
    protected function useAsCallable($value)
1042
    {
1043
        return ! is_string($value) && is_callable($value);
1044
    }
1045
1046
    /**
1047
     * Join all items from the collection using a string. The final items can use a separate glue string.
1048
     *
1049 10
     * @param  string  $glue
1050
     * @param  string  $finalGlue
1051 10
     * @return string
1052
     */
1053
    public function join($glue, $finalGlue = '')
1054
    {
1055
        if ($finalGlue === '') {
1056
            return $this->implode($glue);
1057
        }
1058
1059
        $count = $this->count();
1060 26
1061
        if ($count === 0) {
1062 26
            return '';
1063
        }
1064 26
1065
        if ($count === 1) {
1066 26
            return $this->last();
1067
        }
1068
1069
        $collection = new static($this->items);
1070
1071
        $finalItem = $collection->pop();
1072
1073
        return $collection->implode($glue).$finalGlue.$finalItem;
1074
    }
1075 1
1076
    /**
1077
     * Get the keys of the collection items.
1078 1
     *
1079
     * @return static
1080 1
     */
1081 1
    public function keys()
1082
    {
1083
        return new static(array_keys($this->items));
1084
    }
1085
1086
    /**
1087
     * Get the last item from the collection.
1088
     *
1089
     * @param  callable|null  $callback
1090
     * @param  mixed  $default
1091
     * @return mixed
1092 4
     */
1093
    public function last(callable $callback = null, $default = null)
1094 4
    {
1095
        return Arr::last($this->items, $callback, $default);
1096 4
    }
1097 4
1098
    /**
1099 4
     * Get the values of a given key.
1100
     *
1101 4
     * @param  string|array  $value
1102
     * @param  string|null  $key
1103 4
     * @return static
1104 4
     */
1105
    public function pluck($value, $key = null)
1106
    {
1107 4
        return new static(Arr::pluck($this->items, $value, $key));
1108
    }
1109
1110 4
    /**
1111
     * Run a map over each of the items.
1112
     *
1113
     * @param  callable  $callback
1114
     * @return static
1115
     */
1116
    public function map(callable $callback)
1117
    {
1118
        $keys = array_keys($this->items);
1119
1120
        $items = array_map($callback, $this->items, $keys);
1121 2
1122
        return new static(array_combine($keys, $items));
1123 2
    }
1124
1125 2
    /**
1126
     * Run a map over each nested chunk of items.
1127
     *
1128
     * @param  callable  $callback
1129
     * @return static
1130
     */
1131
    public function mapSpread(callable $callback)
1132
    {
1133
        return $this->map(function ($chunk, $key) use ($callback) {
1134
            $chunk[] = $key;
1135
1136 5
            return $callback(...$chunk);
1137
        });
1138 5
    }
1139
1140 5
    /**
1141 5
     * Run a dictionary map over the items.
1142
     *
1143 5
     * The callback should return an associative array with a single key/value pair.
1144 5
     *
1145
     * @param  callable  $callback
1146
     * @return static
1147
     */
1148 5
    public function mapToDictionary(callable $callback)
1149
    {
1150
        $dictionary = [];
1151
1152
        foreach ($this->items as $key => $item) {
1153
            $pair = $callback($item, $key);
1154
1155
            $key = key($pair);
1156
1157 1
            $value = reset($pair);
1158
1159 1
            if (! isset($dictionary[$key])) {
1160
                $dictionary[$key] = [];
1161
            }
1162
1163
            $dictionary[$key][] = $value;
1164
        }
1165
1166
        return new static($dictionary);
1167
    }
1168 1
1169
    /**
1170
     * Run a grouping map over the items.
1171 1
     *
1172 1
     * The callback should return an associative array with a single key/value pair.
1173
     *
1174
     * @param  callable  $callback
1175
     * @return static
1176
     */
1177
    public function mapToGroups(callable $callback)
1178
    {
1179
        $groups = $this->mapToDictionary($callback);
1180
1181 1
        return $groups->map([$this, 'make']);
1182
    }
1183 1
1184
    /**
1185
     * Run an associative map over each of the items.
1186 1
     *
1187
     * The callback should return an associative array with a single key/value pair.
1188 1
     *
1189
     * @param  callable  $callback
1190 1
     * @return static
1191 1
     */
1192
    public function mapWithKeys(callable $callback)
1193
    {
1194
        $result = [];
1195
1196
        foreach ($this->items as $key => $value) {
1197
            $assoc = $callback($value, $key);
1198
1199
            foreach ($assoc as $mapKey => $mapValue) {
1200 3
                $result[$mapKey] = $mapValue;
1201
            }
1202 3
        }
1203
1204
        return new static($result);
1205
    }
1206
1207
    /**
1208
     * Map a collection and flatten the result by a single level.
1209
     *
1210
     * @param  callable  $callback
1211 2
     * @return static
1212
     */
1213 2
    public function flatMap(callable $callback)
1214
    {
1215
        return $this->map($callback)->collapse();
1216
    }
1217
1218
    /**
1219
     * Map the values into a new class.
1220
     *
1221
     * @param  string  $class
1222 3
     * @return static
1223
     */
1224 3
    public function mapInto($class)
1225
    {
1226
        return $this->map(function ($value, $key) use ($class) {
1227
            return new $class($value, $key);
1228
        });
1229
    }
1230
1231
    /**
1232
     * Get the max value of a given key.
1233 1
     *
1234
     * @param  callable|string|null  $callback
1235 1
     * @return mixed
1236
     */
1237 View Code Duplication
    public function max($callback = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1238 1
    {
1239
        $callback = $this->valueRetriever($callback);
1240 1
1241
        return $this->filter(function ($value) {
1242 1
            return ! is_null($value);
1243 1
        })->reduce(function ($result, $item) use ($callback) {
1244
            $value = $callback($item);
1245
1246
            return is_null($result) || $value > $result ? $value : $result;
1247
        });
1248
    }
1249
1250
    /**
1251
     * Merge the collection with the given items.
1252
     *
1253 1
     * @param  mixed  $items
1254
     * @return static
1255 1
     */
1256
    public function merge($items)
1257 1
    {
1258
        return new static(array_merge($this->items, $this->getArrayableItems($items)));
1259 1
    }
1260 1
1261 1
    /**
1262
     * Recursively merge the collection with the given items.
1263
     *
1264 1
     * @param  mixed  $items
1265
     * @return static
1266
     */
1267 1
    public function mergeRecursive($items)
1268
    {
1269
        return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
1270
    }
1271
1272
    /**
1273
     * Create a collection by using this collection for keys and another for its values.
1274
     *
1275
     * @param  mixed  $values
1276 1
     * @return static
1277
     */
1278 1
    public function combine($values)
1279 1
    {
1280
        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
1281
    }
1282 1
1283 1
    /**
1284
     * Union the collection with the given items.
1285
     *
1286 1
     * @param  mixed  $items
1287
     * @return static
1288 1
     */
1289
    public function union($items)
1290
    {
1291
        return new static($this->items + $this->getArrayableItems($items));
1292
    }
1293
1294
    /**
1295
     * Get the min value of a given key.
1296
     *
1297
     * @param  callable|string|null  $callback
1298 1
     * @return mixed
1299
     */
1300 1 View Code Duplication
    public function min($callback = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1301
    {
1302 1
        $callback = $this->valueRetriever($callback);
1303
1304
        return $this->map(function ($value) use ($callback) {
1305
            return $callback($value);
1306
        })->filter(function ($value) {
1307
            return ! is_null($value);
1308
        })->reduce(function ($result, $value) {
1309
            return is_null($result) || $value < $result ? $value : $result;
1310
        });
1311
    }
1312
1313 7
    /**
1314
     * Create a new collection consisting of every n-th element.
1315 7
     *
1316
     * @param  int  $step
1317 7
     * @param  int  $offset
1318 6
     * @return static
1319 7
     */
1320
    public function nth($step, $offset = 0)
1321 7
    {
1322 6
        $new = [];
1323
1324
        $position = 0;
1325 7
1326
        foreach ($this->items as $item) {
1327
            if ($position % $step === $offset) {
1328
                $new[] = $item;
1329
            }
1330
1331
            $position++;
1332
        }
1333
1334 1
        return new static($new);
1335
    }
1336 1
1337
    /**
1338
     * Get the items with the specified keys.
1339
     *
1340
     * @param  mixed  $keys
1341
     * @return static
1342
     */
1343
    public function only($keys)
1344 2
    {
1345
        if (is_null($keys)) {
1346 2
            return new static($this->items);
1347
        }
1348
1349
        if ($keys instanceof self) {
1350
            $keys = $keys->all();
1351
        }
1352
1353
        $keys = is_array($keys) ? $keys : func_get_args();
1354
1355
        return new static(Arr::only($this->items, $keys));
1356 1
    }
1357
1358 1
    /**
1359
     * "Paginate" the collection by slicing it into a smaller collection.
1360 1
     *
1361
     * @param  int  $page
1362
     * @param  int  $perPage
1363
     * @return static
1364
     */
1365
    public function forPage($page, $perPage)
1366
    {
1367
        $offset = max(0, ($page - 1) * $perPage);
1368
1369 21
        return $this->slice($offset, $perPage);
1370
    }
1371 21
1372
    /**
1373 21
     * Partition the collection into two arrays using the given callback or key.
1374
     *
1375
     * @param  callable|string  $key
1376
     * @param  mixed  $operator
1377
     * @param  mixed  $value
1378
     * @return static
1379
     */
1380
    public function partition($key, $operator = null, $value = null)
1381
    {
1382 2
        $partitions = [new static, new static];
1383
1384 2
        $callback = func_num_args() === 1
1385
                ? $this->valueRetriever($key)
1386 2
                : $this->operatorForWhere(...func_get_args());
1387 2
1388
        foreach ($this->items as $key => $item) {
1389
            $partitions[(int) ! $callback($item, $key)][$key] = $item;
1390 2
        }
1391
1392
        return new static($partitions);
1393
    }
1394
1395
    /**
1396
     * Pass the collection to the given callback and return the result.
1397
     *
1398
     * @param  callable $callback
1399
     * @return mixed
1400 3
     */
1401
    public function pipe(callable $callback)
1402 3
    {
1403
        return $callback($this);
1404
    }
1405
1406
    /**
1407
     * Get and remove the last item from the collection.
1408
     *
1409
     * @return mixed
1410
     */
1411
    public function pop()
1412 3
    {
1413
        return array_pop($this->items);
1414 3
    }
1415
1416 3
    /**
1417
     * Push an item onto the beginning of the collection.
1418
     *
1419
     * @param  mixed  $value
1420
     * @param  mixed  $key
1421
     * @return $this
1422
     */
1423
    public function prepend($value, $key = null)
1424
    {
1425
        $this->items = Arr::prepend($this->items, $value, $key);
1426
1427 3
        return $this;
1428
    }
1429 3
1430 1
    /**
1431
     * Push an item onto the end of the collection.
1432
     *
1433 3
     * @param  mixed  $value
1434
     * @return $this
1435
     */
1436
    public function push($value)
1437
    {
1438
        $this->offsetSet(null, $value);
1439
1440
        return $this;
1441
    }
1442
1443 5
    /**
1444
     * Push all of the given items onto the collection.
1445 5
     *
1446
     * @param  iterable  $source
1447
     * @return static
1448
     */
1449
    public function concat($source)
1450
    {
1451
        $result = new static($this);
1452
1453
        foreach ($source as $item) {
1454 9
            $result->push($item);
1455
        }
1456 9
1457
        return $result;
1458
    }
1459 9
1460 8
    /**
1461 9
     * Get and remove an item from the collection.
1462 9
     *
1463
     * @param  mixed  $key
1464
     * @param  mixed  $default
1465
     * @return mixed
1466
     */
1467
    public function pull($key, $default = null)
1468
    {
1469
        return Arr::pull($this->items, $key, $default);
1470 1
    }
1471
1472 1
    /**
1473
     * Put an item in the collection by key.
1474
     *
1475
     * @param  mixed  $key
1476
     * @param  mixed  $value
1477
     * @return $this
1478
     */
1479
    public function put($key, $value)
1480
    {
1481
        $this->offsetSet($key, $value);
1482 3
1483
        return $this;
1484 3
    }
1485 3
1486
    /**
1487
     * Get one or a specified number of items randomly from the collection.
1488 2
     *
1489 2
     * @param  int|null  $number
1490 1
     * @return static|mixed
1491
     *
1492
     * @throws \InvalidArgumentException
1493
     */
1494 1
    public function random($number = null)
1495
    {
1496
        if (is_null($number)) {
1497
            return Arr::random($this->items);
1498
        }
1499
1500
        return new static(Arr::random($this->items, $number));
1501
    }
1502 1
1503
    /**
1504 1
     * Reduce the collection to a single value.
1505
     *
1506
     * @param  callable  $callback
1507
     * @param  mixed  $initial
1508
     * @return mixed
1509
     */
1510
    public function reduce(callable $callback, $initial = null)
1511
    {
1512
        return array_reduce($this->items, $callback, $initial);
1513 1
    }
1514
1515 1
    /**
1516
     * Create a collection of all elements that do not pass a given truth test.
1517
     *
1518
     * @param  callable|mixed  $callback
1519
     * @return static
1520
     */
1521
    public function reject($callback = true)
1522
    {
1523
        $useAsCallable = $this->useAsCallable($callback);
1524
1525 10
        return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
1526
            return $useAsCallable
1527 10
                ? ! $callback($value, $key)
1528
                : $value != $callback;
1529
        });
1530
    }
1531
1532
    /**
1533
     * Replace the collection items with the given items.
1534
     *
1535
     * @param  mixed  $items
1536 7
     * @return static
1537
     */
1538 7
    public function replace($items)
1539 1
    {
1540
        return new static(array_replace($this->items, $this->getArrayableItems($items)));
1541
    }
1542 6
1543
    /**
1544 6
     * Recursively replace the collection items with the given items.
1545
     *
1546 6
     * @param  mixed  $items
1547
     * @return static
1548 6
     */
1549
    public function replaceRecursive($items)
1550 6
    {
1551 6
        return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
1552
    }
1553 6
1554 5
    /**
1555
     * Reverse items order.
1556
     *
1557 6
     * @return static
1558 6
     */
1559
    public function reverse()
1560 6
    {
1561
        return new static(array_reverse($this->items, true));
1562
    }
1563
1564 6
    /**
1565
     * Search the collection for a given value and return the corresponding key if successful.
1566
     *
1567
     * @param  mixed  $value
1568
     * @param  bool  $strict
1569
     * @return mixed
1570
     */
1571
    public function search($value, $strict = false)
1572
    {
1573 3
        if (! $this->useAsCallable($value)) {
1574
            return array_search($value, $this->items, $strict);
1575 3
        }
1576 2
1577
        foreach ($this->items as $key => $item) {
1578
            if (call_user_func($value, $item, $key)) {
1579 1
                return $key;
1580
            }
1581 1
        }
1582 1
1583
        return false;
1584
    }
1585 1
1586
    /**
1587
     * Get and remove the first item from the collection.
1588
     *
1589
     * @return mixed
1590
     */
1591
    public function shift()
1592
    {
1593
        return array_shift($this->items);
1594 11
    }
1595
1596 11
    /**
1597
     * Shuffle the items in the collection.
1598 11
     *
1599 1
     * @param  int  $seed
1600 10
     * @return static
1601
     */
1602 11
    public function shuffle($seed = null)
1603
    {
1604
        return new static(Arr::shuffle($this->items, $seed));
1605
    }
1606
1607
    /**
1608
     * Slice the underlying collection array.
1609
     *
1610
     * @param  int  $offset
1611
     * @param  int  $length
1612
     * @return static
1613 5
     */
1614
    public function slice($offset, $length = null)
1615 5
    {
1616
        return new static(array_slice($this->items, $offset, $length, true));
1617 5
    }
1618
1619
    /**
1620
     * Split a collection into a certain number of groups.
1621
     *
1622 5
     * @param  int  $numberOfGroups
1623 5
     * @return static
1624
     */
1625
    public function split($numberOfGroups)
1626 5
    {
1627 5
        if ($this->isEmpty()) {
1628
            return new static;
1629
        }
1630
1631
        $groups = new static;
1632 5
1633 5
        $groupSize = floor($this->count() / $numberOfGroups);
1634
1635
        $remain = $this->count() % $numberOfGroups;
1636 5
1637
        $start = 0;
1638
1639
        for ($i = 0; $i < $numberOfGroups; $i++) {
1640
            $size = $groupSize;
1641
1642
            if ($i < $remain) {
1643
                $size++;
1644
            }
1645
1646 1
            if ($size) {
1647
                $groups->push(new static(array_slice($this->items, $start, $size)));
1648 1
1649
                $start += $size;
1650
            }
1651
        }
1652
1653
        return $groups;
1654
    }
1655
1656
    /**
1657
     * Chunk the underlying collection array.
1658 2
     *
1659
     * @param  int  $size
1660 2
     * @return static
1661
     */
1662 2
    public function chunk($size)
1663
    {
1664 2
        if ($size <= 0) {
1665
            return new static;
1666
        }
1667
1668
        $chunks = [];
1669
1670
        foreach (array_chunk($this->items, $size, true) as $chunk) {
1671
            $chunks[] = new static($chunk);
1672
        }
1673
1674
        return new static($chunks);
1675
    }
1676
1677
    /**
1678
     * Sort through each item with a callback.
1679
     *
1680
     * @param  callable|null  $callback
1681
     * @return static
1682
     */
1683
    public function sort(callable $callback = null)
1684
    {
1685
        $items = $this->items;
1686 1
1687
        $callback
1688 1
            ? uasort($items, $callback)
1689 1
            : asort($items);
1690
1691
        return new static($items);
1692 1
    }
1693
1694
    /**
1695
     * Sort the collection using the given callback.
1696
     *
1697
     * @param  callable|string  $callback
1698
     * @param  int  $options
1699
     * @param  bool  $descending
1700
     * @return static
1701 8
     */
1702
    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
1703 8
    {
1704 6
        $results = [];
1705
1706
        $callback = $this->valueRetriever($callback);
1707 2
1708
        // First we will loop through the items and get the comparator from a callback
1709
        // function which we were given. Then, we will sort the returned values and
1710 1
        // and grab the corresponding values for the sorted keys from this array.
1711 2
        foreach ($this->items as $key => $value) {
1712
            $results[$key] = $callback($value, $key);
1713
        }
1714
1715
        $descending ? arsort($results, $options)
1716
            : asort($results, $options);
1717
1718
        // Once we have sorted all of the keys in the array, we will loop through them
1719
        // and grab the corresponding model so we can set the underlying items list
1720 2
        // to the sorted version. Then we'll just return the collection instance.
1721
        foreach (array_keys($results) as $key) {
1722 2
            $results[$key] = $this->items[$key];
1723 1
        }
1724
1725
        return new static($results);
1726 1
    }
1727
1728
    /**
1729
     * Sort the collection in descending order using the given callback.
1730
     *
1731
     * @param  callable|string  $callback
1732
     * @param  int  $options
1733
     * @return static
1734
     */
1735 1
    public function sortByDesc($callback, $options = SORT_REGULAR)
1736
    {
1737 1
        return $this->sortBy($callback, $options, true);
1738
    }
1739 1
1740
    /**
1741
     * Sort the collection keys.
1742
     *
1743
     * @param  int  $options
1744
     * @param  bool  $descending
1745
     * @return static
1746
     */
1747
    public function sortKeys($options = SORT_REGULAR, $descending = false)
1748 1
    {
1749
        $items = $this->items;
1750 1
1751
        $descending ? krsort($items, $options) : ksort($items, $options);
1752 1
1753
        return new static($items);
1754
    }
1755
1756
    /**
1757
     * Sort the collection keys in descending order.
1758
     *
1759
     * @param  int $options
1760
     * @return static
1761
     */
1762 5
    public function sortKeysDesc($options = SORT_REGULAR)
1763
    {
1764 5
        return $this->sortKeys($options, true);
1765
    }
1766 5
1767
    /**
1768
     * Splice a portion of the underlying collection array.
1769 5
     *
1770 5
     * @param  int  $offset
1771
     * @param  int|null  $length
1772
     * @param  mixed  $replacement
1773 5
     * @return static
1774 5
     */
1775
    public function splice($offset, $length = null, $replacement = [])
1776
    {
1777
        if (func_num_args() === 1) {
1778
            return new static(array_splice($this->items, $offset));
1779
        }
1780
1781
        return new static(array_splice($this->items, $offset, $length, $replacement));
1782
    }
1783 1
1784
    /**
1785 1
     * Get the sum of the given values.
1786
     *
1787
     * @param  callable|string|null  $callback
1788
     * @return mixed
1789
     */
1790
    public function sum($callback = null)
1791
    {
1792
        if (is_null($callback)) {
1793 39
            return array_sum($this->items);
1794
        }
1795 39
1796
        $callback = $this->valueRetriever($callback);
1797
1798
        return $this->reduce(function ($result, $item) use ($callback) {
1799
            return $result + $callback($item);
1800
        }, 0);
1801
    }
1802
1803
    /**
1804 38
     * Take the first or last {$limit} items.
1805
     *
1806 38
     * @param  int  $limit
1807 25
     * @return static
1808
     */
1809
    public function take($limit)
1810
    {
1811 21
        if ($limit < 0) {
1812 22
            return $this->slice($limit, abs($limit));
1813
        }
1814
1815
        return $this->slice(0, $limit);
1816
    }
1817
1818
    /**
1819
     * Pass the collection to the given callback and then return it.
1820
     *
1821
     * @param  callable  $callback
1822
     * @return $this
1823
     */
1824 1
    public function tap(callable $callback)
1825
    {
1826
        $callback(new static($this->items));
1827 1
1828 1
        return $this;
1829
    }
1830
1831 1
    /**
1832 1
     * Transform each item in the collection using a callback.
1833
     *
1834 1
     * @param  callable  $callback
1835
     * @return $this
1836
     */
1837
    public function transform(callable $callback)
1838
    {
1839
        $this->items = $this->map($callback)->all();
1840
1841
        return $this;
1842
    }
1843
1844 1
    /**
1845
     * Return only unique items from the collection array.
1846 1
     *
1847
     * @param  string|callable|null  $key
1848
     * @param  bool  $strict
1849
     * @return static
1850
     */
1851
    public function unique($key = null, $strict = false)
1852
    {
1853
        $callback = $this->valueRetriever($key);
1854 60
1855
        $exists = [];
1856
1857 57
        return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
1858 60
            if (in_array($id = $callback($item, $key), $exists, $strict)) {
1859
                return true;
1860
            }
1861
1862
            $exists[] = $id;
1863
        });
1864
    }
1865
1866 2
    /**
1867
     * Return only unique items from the collection array using strict comparison.
1868
     *
1869 2
     * @param  string|callable|null  $key
1870 2
     * @return static
1871 2
     */
1872 1
    public function uniqueStrict($key = null)
1873 2
    {
1874 2
        return $this->unique($key, true);
1875
    }
1876
1877 1
    /**
1878 2
     * Reset the keys on the underlying array.
1879
     *
1880
     * @return static
1881
     */
1882
    public function values()
1883
    {
1884
        return new static(array_values($this->items));
1885
    }
1886
1887 2
    /**
1888
     * Get a value retrieving callback.
1889 2
     *
1890
     * @param  callable|string|null  $value
1891
     * @return callable
1892
     */
1893
    protected function valueRetriever($value)
1894
    {
1895
        if ($this->useAsCallable($value)) {
1896
            return $value;
1897 5
        }
1898
1899 5
        return function ($item) use ($value) {
1900
            return data_get($item, $value);
0 ignored issues
show
Documentation introduced by
$value is of type callable|null, but the function expects a string|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1901
        };
1902
    }
1903
1904
    /**
1905
     * Zip the collection together with one or more arrays.
1906
     *
1907
     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1908 1
     *      => [[1, 4], [2, 5], [3, 6]]
1909
     *
1910 1
     * @param  mixed ...$items
1911
     * @return static
1912
     */
1913
    public function zip($items)
1914
    {
1915
        $arrayableItems = array_map(function ($items) {
1916
            return $this->getArrayableItems($items);
1917
        }, func_get_args());
1918 29
1919
        $params = array_merge([function () {
1920 29
            return new static(func_get_args());
1921
        }, $this->items], $arrayableItems);
1922
1923
        return new static(call_user_func_array('array_map', $params));
1924
    }
1925
1926
    /**
1927
     * Pad collection to the specified length with a value.
1928
     *
1929 2
     * @param  int  $size
1930
     * @param  mixed  $value
1931 2
     * @return static
1932
     */
1933 1
    public function pad($size, $value)
1934 1
    {
1935
        return new static(array_pad($this->items, $size, $value));
1936
    }
1937
1938 2
    /**
1939 2
     * Get the collection of items as a plain array.
1940
     *
1941
     * @return array
1942
     */
1943
    public function toArray()
1944
    {
1945
        return array_map(function ($value) {
1946
            return $value instanceof Arrayable ? $value->toArray() : $value;
1947
        }, $this->items);
1948
    }
1949
1950
    /**
1951
     * Convert the object into something JSON serializable.
1952
     *
1953
     * @return array
1954
     */
1955
    public function jsonSerialize()
1956
    {
1957
        return array_map(function ($value) {
1958
            if ($value instanceof JsonSerializable) {
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
1959
                return $value->jsonSerialize();
1960
            } elseif ($value instanceof Jsonable) {
1961
                return json_decode($value->toJson(), true);
1962
            } elseif ($value instanceof Arrayable) {
1963
                return $value->toArray();
1964
            }
1965
1966
            return $value;
1967
        }, $this->items);
1968
    }
1969
1970
    /**
1971 17
     * Get the collection of items as JSON.
1972
     *
1973 17
     * @param  int  $options
1974
     * @return string
1975
     */
1976
    public function toJson($options = 0)
1977
    {
1978
        return json_encode($this->jsonSerialize(), $options);
1979
    }
1980
1981
    /**
1982 18
     * Get an iterator for the items.
1983
     *
1984 18
     * @return \ArrayIterator
1985
     */
1986
    public function getIterator()
1987
    {
1988
        return new ArrayIterator($this->items);
1989
    }
1990
1991
    /**
1992
     * Get a CachingIterator instance.
1993
     *
1994 47
     * @param  int  $flags
1995
     * @return \CachingIterator
1996 47
     */
1997 32
    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1998
    {
1999 17
        return new CachingIterator($this->getIterator(), $flags);
2000
    }
2001 47
2002
    /**
2003
     * Count the number of items in the collection.
2004
     *
2005
     * @return int
2006
     */
2007
    public function count()
2008
    {
2009 4
        return count($this->items);
2010
    }
2011 4
2012 4
    /**
2013
     * Count the number of items in the collection using a given truth test.
2014
     *
2015
     * @param  callable|null  $callback
2016
     * @return static
2017
     */
2018
    public function countBy($callback = null)
2019 1
    {
2020
        if (is_null($callback)) {
2021 1
            $callback = function ($value) {
2022
                return $value;
2023
            };
2024
        }
2025
2026
        return new static($this->groupBy($callback)->map(function ($value) {
2027
            return $value->count();
2028
        }));
2029
    }
2030 238
2031
    /**
2032 238
     * Add an item to the collection.
2033 232
     *
2034 37
     * @param  mixed  $item
2035 22
     * @return $this
2036 16
     */
2037 1
    public function add($item)
2038 16
    {
2039 1
        $this->items[] = $item;
2040 16
2041 1
        return $this;
2042 15
    }
2043 2
2044
    /**
2045
     * Get a base Support collection instance from this collection.
2046 13
     *
2047
     * @return \IlluminateAgnostic\Str\Support\Collection
2048
     */
2049
    public function toBase()
2050
    {
2051
        return new self($this);
2052
    }
2053
2054
    /**
2055 1
     * Determine if an item exists at an offset.
2056
     *
2057 1
     * @param  mixed  $key
2058 1
     * @return bool
2059
     */
2060
    public function offsetExists($key)
2061
    {
2062
        return array_key_exists($key, $this->items);
2063
    }
2064
2065
    /**
2066
     * Get an item at a given offset.
2067
     *
2068 16
     * @param  mixed  $key
2069
     * @return mixed
2070 16
     */
2071 1
    public function offsetGet($key)
2072
    {
2073
        return $this->items[$key];
2074 15
    }
2075
2076
    /**
2077
     * Set the item at a given offset.
2078
     *
2079
     * @param  mixed  $key
2080
     * @param  mixed  $value
2081
     * @return void
2082
     */
2083
    public function offsetSet($key, $value)
2084
    {
2085
        if (is_null($key)) {
2086
            $this->items[] = $value;
2087
        } else {
2088
            $this->items[$key] = $value;
2089
        }
2090
    }
2091
2092
    /**
2093
     * Unset the item at a given offset.
2094
     *
2095
     * @param  string  $key
2096
     * @return void
2097
     */
2098
    public function offsetUnset($key)
2099
    {
2100
        unset($this->items[$key]);
2101
    }
2102
2103
    /**
2104
     * Convert the collection to its string representation.
2105
     *
2106
     * @return string
2107
     */
2108
    public function __toString()
2109
    {
2110
        return $this->toJson();
2111
    }
2112
2113
    /**
2114
     * Results array of items from Collection or Arrayable.
2115
     *
2116
     * @param  mixed  $items
2117
     * @return array
2118
     */
2119
    protected function getArrayableItems($items)
2120
    {
2121
        if (is_array($items)) {
2122
            return $items;
2123
        } elseif ($items instanceof self) {
2124
            return $items->all();
2125
        } elseif ($items instanceof Arrayable) {
2126
            return $items->toArray();
2127
        } elseif ($items instanceof Jsonable) {
2128
            return json_decode($items->toJson(), true);
2129
        } elseif ($items instanceof JsonSerializable) {
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
2130
            return (array) $items->jsonSerialize();
2131
        } elseif ($items instanceof Traversable) {
2132
            return iterator_to_array($items);
2133
        }
2134
2135
        return (array) $items;
2136
    }
2137
2138
    /**
2139
     * Add a method to the list of proxied methods.
2140
     *
2141
     * @param  string  $method
2142
     * @return void
2143
     */
2144
    public static function proxy($method)
2145
    {
2146
        static::$proxies[] = $method;
2147
    }
2148
2149
    /**
2150
     * Dynamically access collection proxies.
2151
     *
2152
     * @param  string  $key
2153
     * @return mixed
2154
     *
2155
     * @throws \Exception
2156
     */
2157
    public function __get($key)
2158
    {
2159
        if (! in_array($key, static::$proxies)) {
2160
            throw new Exception("Property [{$key}] does not exist on this collection instance.");
2161
        }
2162
2163
        return new HigherOrderCollectionProxy($this, $key);
2164
    }
2165
}
2166