Collection::splice()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace MiotApi\Util\Collection;
4
5
use ArrayAccess;
6
use ArrayIterator;
7
use CachingIterator;
8
use Countable;
9
use Exception;
10
use IteratorAggregate;
11
use JsonSerializable;
12
use stdClass;
13
use Traversable;
14
15
/**
16
 * Class Collection.
17
 */
18
class Collection implements ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
19
{
20
    use Macroable;
0 ignored issues
show
Bug introduced by
The trait MiotApi\Util\Collection\Macroable requires the property $name which is not provided by MiotApi\Util\Collection\Collection.
Loading history...
21
22
    /**
23
     * The items contained in the collection.
24
     *
25
     * @var array
26
     */
27
    protected $items = [];
28
29
    /**
30
     * The methods that can be proxied.
31
     *
32
     * @var array
33
     */
34
    protected static $proxies = [
35
        'average',
36
        'avg',
37
        'contains',
38
        'each',
39
        'every',
40
        'filter',
41
        'first',
42
        'flatMap',
43
        'keyBy',
44
        'map',
45
        'partition',
46
        'reject',
47
        'sortBy',
48
        'sortByDesc',
49
        'sum',
50
        'unique',
51
    ];
52
53
    /**
54
     * Create a new collection.
55
     *
56
     * @param mixed $items
57
     */
58
    public function __construct($items = [])
59
    {
60
        $this->items = $this->getArrayableItems($items);
61
    }
62
63
    /**
64
     * Create a new collection instance if the value isn't one already.
65
     *
66
     * @param mixed $items
67
     *
68
     * @return static
69
     */
70
    public static function make($items = [])
71
    {
72
        return new static($items);
73
    }
74
75
    /**
76
     * Wrap the given value in a collection if applicable.
77
     *
78
     * @param mixed $value
79
     *
80
     * @return static
81
     */
82
    public static function wrap($value)
83
    {
84
        return $value instanceof self
85
            ? new static($value)
86
            : new static(Arr::wrap($value));
87
    }
88
89
    /**
90
     * Get the underlying items from the given collection if applicable.
91
     *
92
     * @param array|static $value
93
     *
94
     * @return array
95
     */
96
    public static function unwrap($value)
97
    {
98
        return $value instanceof self ? $value->all() : $value;
99
    }
100
101
    /**
102
     * Create a new collection by invoking the callback a given amount of times.
103
     *
104
     * @param int      $number
105
     * @param callable $callback
106
     *
107
     * @return static
108
     */
109
    public static function times($number, callable $callback = null)
110
    {
111
        if ($number < 1) {
112
            return new static();
113
        }
114
115
        if (is_null($callback)) {
116
            return new static(range(1, $number));
117
        }
118
119
        return (new static(range(1, $number)))->map($callback);
120
    }
121
122
    /**
123
     * Get all of the items in the collection.
124
     *
125
     * @return array
126
     */
127
    public function all()
128
    {
129
        return $this->items;
130
    }
131
132
    /**
133
     * Get the average value of a given key.
134
     *
135
     * @param callable|string|null $callback
136
     *
137
     * @return mixed
138
     */
139
    public function avg($callback = null)
140
    {
141
        if ($count = $this->count()) {
142
            return $this->sum($callback) / $count;
143
        }
144
    }
145
146
    /**
147
     * Alias for the "avg" method.
148
     *
149
     * @param callable|string|null $callback
150
     *
151
     * @return mixed
152
     */
153
    public function average($callback = null)
154
    {
155
        return $this->avg($callback);
156
    }
157
158
    /**
159
     * Get the mode of a given key.
160
     *
161
     * @param mixed $key
162
     *
163
     * @return array|null
164
     */
165
    public function mode($key = null)
166
    {
167
        $count = $this->count();
168
169
        if ($count == 0) {
170
            return;
171
        }
172
173
        $collection = isset($key) ? $this->pluck($key) : $this;
174
175
        $counts = new self();
176
177
        $collection->each(function ($value) use ($counts) {
178
            $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
179
        });
180
181
        $sorted = $counts->sort();
182
183
        $highestValue = $sorted->last();
184
185
        return $sorted->filter(function ($value) use ($highestValue) {
186
            return $value == $highestValue;
187
        })->sort()->keys()->all();
188
    }
189
190
    /**
191
     * Collapse the collection of items into a single array.
192
     *
193
     * @return static
194
     */
195
    public function collapse()
196
    {
197
        return new static(Arr::collapse($this->items));
198
    }
199
200
    /**
201
     * Determine if an item exists in the collection.
202
     *
203
     * @param mixed $key
204
     * @param mixed $operator
205
     * @param mixed $value
206
     *
207
     * @return bool
208
     */
209
    public function contains($key, $operator = null, $value = null)
210
    {
211
        if (func_num_args() == 1) {
212
            if ($this->useAsCallable($key)) {
213
                $placeholder = new stdClass();
214
215
                return $this->first($key, $placeholder) !== $placeholder;
216
            }
217
218
            return in_array($key, $this->items);
219
        }
220
221
        return $this->contains($this->operatorForWhere(...func_get_args()));
222
    }
223
224
    /**
225
     * Determine if an item exists in the collection using strict comparison.
226
     *
227
     * @param mixed $key
228
     * @param mixed $value
229
     *
230
     * @return bool
231
     */
232
    public function containsStrict($key, $value = null)
233
    {
234
        if (func_num_args() == 2) {
235
            return $this->contains(function ($item) use ($key, $value) {
236
                return Arr::data_get($item, $key) === $value;
237
            });
238
        }
239
240
        if ($this->useAsCallable($key)) {
241
            return !is_null($this->first($key));
242
        }
243
244
        return in_array($key, $this->items, true);
245
    }
246
247
    /**
248
     * Cross join with the given lists, returning all possible permutations.
249
     *
250
     * @param mixed ...$lists
251
     *
252
     * @return static
253
     */
254
    public function crossJoin(...$lists)
255
    {
256
        return new static(Arr::crossJoin(
257
            $this->items,
258
            ...array_map([$this, 'getArrayableItems'], $lists)
259
        ));
260
    }
261
262
    /**
263
     * Dump the collection and end the script.
264
     *
265
     * @return void
266
     */
267
    public function dd(...$args)
268
    {
269
        http_response_code(500);
270
271
        call_user_func_array([$this, 'dump'], $args);
272
273
        die(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
274
    }
275
276
    /**
277
     * Dump the collection.
278
     *
279
     * @return $this
280
     */
281
    public function dump()
282
    {
283
        (new static(func_get_args()))
284
            ->push($this)
285
            ->each(function ($item) {
286
                var_dump($item);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($item) looks like debug code. Are you sure you do not want to remove it?
Loading history...
287
            });
288
289
        return $this;
290
    }
291
292
    /**
293
     * Get the items in the collection that are not present in the given items.
294
     *
295
     * @param mixed $items
296
     *
297
     * @return static
298
     */
299
    public function diff($items)
300
    {
301
        return new static(array_diff($this->items, $this->getArrayableItems($items)));
302
    }
303
304
    /**
305
     * Get the items in the collection whose keys and values are not present in the given items.
306
     *
307
     * @param mixed $items
308
     *
309
     * @return static
310
     */
311
    public function diffAssoc($items)
312
    {
313
        return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
314
    }
315
316
    /**
317
     * Get the items in the collection whose keys are not present in the given items.
318
     *
319
     * @param mixed $items
320
     *
321
     * @return static
322
     */
323
    public function diffKeys($items)
324
    {
325
        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
326
    }
327
328
    /**
329
     * Execute a callback over each item.
330
     *
331
     * @param callable $callback
332
     *
333
     * @return $this
334
     */
335
    public function each(callable $callback)
336
    {
337
        foreach ($this->items as $key => $item) {
338
            if ($callback($item, $key) === false) {
339
                break;
340
            }
341
        }
342
343
        return $this;
344
    }
345
346
    /**
347
     * Execute a callback over each nested chunk of items.
348
     *
349
     * @param callable $callback
350
     *
351
     * @return static
352
     */
353
    public function eachSpread(callable $callback)
354
    {
355
        return $this->each(function ($chunk, $key) use ($callback) {
356
            $chunk[] = $key;
357
358
            return $callback(...$chunk);
359
        });
360
    }
361
362
    /**
363
     * Determine if all items in the collection pass the given test.
364
     *
365
     * @param string|callable $key
366
     * @param mixed           $operator
367
     * @param mixed           $value
368
     *
369
     * @return bool
370
     */
371
    public function every($key, $operator = null, $value = null)
372
    {
373
        if (func_num_args() == 1) {
374
            $callback = $this->valueRetriever($key);
375
376
            foreach ($this->items as $k => $v) {
377
                if (!$callback($v, $k)) {
378
                    return false;
379
                }
380
            }
381
382
            return true;
383
        }
384
385
        return $this->every($this->operatorForWhere(...func_get_args()));
386
    }
387
388
    /**
389
     * Get all items except for those with the specified keys.
390
     *
391
     * @param  $keys
392
     *
393
     * @return static
394
     */
395
    public function except($keys)
396
    {
397
        if ($keys instanceof self) {
398
            $keys = $keys->all();
399
        } elseif (!is_array($keys)) {
400
            $keys = func_get_args();
401
        }
402
403
        return new static(Arr::except($this->items, $keys));
404
    }
405
406
    /**
407
     * Run a filter over each of the items.
408
     *
409
     * @param callable|null $callback
410
     *
411
     * @return static
412
     */
413
    public function filter(callable $callback = null)
414
    {
415
        if ($callback) {
416
            return new static(Arr::where($this->items, $callback));
417
        }
418
419
        return new static(array_filter($this->items));
420
    }
421
422
    /**
423
     * Apply the callback if the value is truthy.
424
     *
425
     * @param bool     $value
426
     * @param callable $callback
427
     * @param callable $default
428
     *
429
     * @return mixed
430
     */
431
    public function when($value, callable $callback, callable $default = null)
432
    {
433
        if ($value) {
434
            return $callback($this, $value);
435
        } elseif ($default) {
436
            return $default($this, $value);
437
        }
438
439
        return $this;
440
    }
441
442
    /**
443
     * Apply the callback if the value is falsy.
444
     *
445
     * @param bool     $value
446
     * @param callable $callback
447
     * @param callable $default
448
     *
449
     * @return mixed
450
     */
451
    public function unless($value, callable $callback, callable $default = null)
452
    {
453
        return $this->when(!$value, $callback, $default);
454
    }
455
456
    /**
457
     * Filter items by the given key value pair.
458
     *
459
     * @param string $key
460
     * @param mixed  $operator
461
     * @param mixed  $value
462
     *
463
     * @return static
464
     */
465
    public function where($key, $operator, $value = null)
466
    {
467
        return $this->filter($this->operatorForWhere(...func_get_args()));
468
    }
469
470
    /**
471
     * Get an operator checker callback.
472
     *
473
     * @param string $key
474
     * @param string $operator
475
     * @param mixed  $value
476
     *
477
     * @return \Closure
478
     */
479
    protected function operatorForWhere($key, $operator, $value = null)
480
    {
481
        if (func_num_args() == 2) {
482
            $value = $operator;
483
484
            $operator = '=';
485
        }
486
487
        /*
488
         * @param $item
489
         * @return bool
490
         */
491
        return function ($item) use ($key, $operator, $value) {
492
            $retrieved = Arr::data_get($item, $key);
493
494
            $strings = array_filter([$retrieved, $value], function ($value) {
495
                return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
496
            });
497
498
            if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
499
                return in_array($operator, ['!=', '<>', '!==']);
500
            }
501
502
            switch ($operator) {
503
                default:
504
                case '=':
505
                case '==':
506
                    return $retrieved == $value;
507
                case '!=':
508
                case '<>':
509
                    return $retrieved != $value;
510
                case '<':
511
                    return $retrieved < $value;
512
                case '>':
513
                    return $retrieved > $value;
514
                case '<=':
515
                    return $retrieved <= $value;
516
                case '>=':
517
                    return $retrieved >= $value;
518
                case '===':
519
                    return $retrieved === $value;
520
                case '!==':
521
                    return $retrieved !== $value;
522
            }
523
        };
524
    }
525
526
    /**
527
     * Filter items by the given key value pair using strict comparison.
528
     *
529
     * @param string $key
530
     * @param mixed  $value
531
     *
532
     * @return static
533
     */
534
    public function whereStrict($key, $value)
535
    {
536
        return $this->where($key, '===', $value);
537
    }
538
539
    /**
540
     * Filter items by the given key value pair.
541
     *
542
     * @param string $key
543
     * @param mixed  $values
544
     * @param bool   $strict
545
     *
546
     * @return static
547
     */
548
    public function whereIn($key, $values, $strict = false)
549
    {
550
        $values = $this->getArrayableItems($values);
551
552
        return $this->filter(function ($item) use ($key, $values, $strict) {
553
            return in_array(Arr::data_get($item, $key), $values, $strict);
554
        });
555
    }
556
557
    /**
558
     * Filter items by the given key value pair using strict comparison.
559
     *
560
     * @param string $key
561
     * @param mixed  $values
562
     *
563
     * @return static
564
     */
565
    public function whereInStrict($key, $values)
566
    {
567
        return $this->whereIn($key, $values, true);
568
    }
569
570
    /**
571
     * Filter items by the given key value pair.
572
     *
573
     * @param string $key
574
     * @param mixed  $values
575
     * @param bool   $strict
576
     *
577
     * @return static
578
     */
579
    public function whereNotIn($key, $values, $strict = false)
580
    {
581
        $values = $this->getArrayableItems($values);
582
583
        return $this->reject(function ($item) use ($key, $values, $strict) {
584
            return in_array(Arr::data_get($item, $key), $values, $strict);
585
        });
586
    }
587
588
    /**
589
     * Filter items by the given key value pair using strict comparison.
590
     *
591
     * @param string $key
592
     * @param mixed  $values
593
     *
594
     * @return static
595
     */
596
    public function whereNotInStrict($key, $values)
597
    {
598
        return $this->whereNotIn($key, $values, true);
599
    }
600
601
    /**
602
     * Get the first item from the collection.
603
     *
604
     * @param callable|null $callback
605
     * @param mixed         $default
606
     *
607
     * @return mixed
608
     */
609
    public function first(callable $callback = null, $default = null)
610
    {
611
        return Arr::first($this->items, $callback, $default);
612
    }
613
614
    /**
615
     * Get the first item by the given key value pair.
616
     *
617
     * @param string $key
618
     * @param mixed  $operator
619
     * @param mixed  $value
620
     *
621
     * @return static
622
     */
623
    public function firstWhere($key, $operator, $value = null)
624
    {
625
        return $this->first($this->operatorForWhere(...func_get_args()));
626
    }
627
628
    /**
629
     * Get a flattened array of the items in the collection.
630
     *
631
     * @param int $depth
632
     *
633
     * @return static
634
     */
635
    public function flatten($depth = INF)
636
    {
637
        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 MiotApi\Util\Collection\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

637
        return new static(Arr::flatten($this->items, /** @scrutinizer ignore-type */ $depth));
Loading history...
638
    }
639
640
    /**
641
     * Flip the items in the collection.
642
     *
643
     * @return static
644
     */
645
    public function flip()
646
    {
647
        return new static(array_flip($this->items));
648
    }
649
650
    /**
651
     * Remove an item from the collection by key.
652
     *
653
     * @param string|array $keys
654
     *
655
     * @return $this
656
     */
657
    public function forget($keys)
658
    {
659
        foreach ((array) $keys as $key) {
660
            $this->offsetUnset($key);
661
        }
662
663
        return $this;
664
    }
665
666
    /**
667
     * Get an item from the collection by key.
668
     *
669
     * @param mixed $key
670
     * @param mixed $default
671
     *
672
     * @return mixed
673
     */
674
    public function get($key, $default = null)
675
    {
676
        if ($this->offsetExists($key)) {
677
            return $this->items[$key];
678
        }
679
680
        return Arr::value($default);
681
    }
682
683
    /**
684
     * Group an associative array by a field or using a callback.
685
     *
686
     * @param callable|string $groupBy
687
     * @param bool            $preserveKeys
688
     *
689
     * @return static
690
     */
691
    public function groupBy($groupBy, $preserveKeys = false)
692
    {
693
        if (is_array($groupBy)) {
694
            $nextGroups = $groupBy;
695
696
            $groupBy = array_shift($nextGroups);
697
        }
698
699
        $groupBy = $this->valueRetriever($groupBy);
0 ignored issues
show
Bug introduced by
It seems like $groupBy can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

699
        $groupBy = $this->valueRetriever(/** @scrutinizer ignore-type */ $groupBy);
Loading history...
700
701
        $results = [];
702
703
        foreach ($this->items as $key => $value) {
704
            $groupKeys = $groupBy($value, $key);
705
706
            if (!is_array($groupKeys)) {
707
                $groupKeys = [$groupKeys];
708
            }
709
710
            foreach ($groupKeys as $groupKey) {
711
                $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
712
713
                if (!array_key_exists($groupKey, $results)) {
714
                    $results[$groupKey] = new static();
715
                }
716
717
                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
718
            }
719
        }
720
721
        $result = new static($results);
722
723
        if (!empty($nextGroups)) {
724
            return $result->map->groupBy($nextGroups, $preserveKeys);
0 ignored issues
show
Bug Best Practice introduced by
The property map does not exist on MiotApi\Util\Collection\Collection. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method groupBy() does not exist on MiotApi\Util\Collection\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

724
            return $result->map->/** @scrutinizer ignore-call */ groupBy($nextGroups, $preserveKeys);
Loading history...
725
        }
726
727
        return $result;
728
    }
729
730
    /**
731
     * Key an associative array by a field or using a callback.
732
     *
733
     * @param callable|string $keyBy
734
     *
735
     * @return static
736
     */
737
    public function keyBy($keyBy)
738
    {
739
        $keyBy = $this->valueRetriever($keyBy);
0 ignored issues
show
Bug introduced by
It seems like $keyBy can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

739
        $keyBy = $this->valueRetriever(/** @scrutinizer ignore-type */ $keyBy);
Loading history...
740
741
        $results = [];
742
743
        foreach ($this->items as $key => $item) {
744
            $resolvedKey = $keyBy($item, $key);
745
746
            if (is_object($resolvedKey)) {
747
                $resolvedKey = (string) $resolvedKey;
748
            }
749
750
            $results[$resolvedKey] = $item;
751
        }
752
753
        return new static($results);
754
    }
755
756
    /**
757
     * Determine if an item exists in the collection by key.
758
     *
759
     * @param mixed $key
760
     *
761
     * @return bool
762
     */
763
    public function has($key)
764
    {
765
        $keys = is_array($key) ? $key : func_get_args();
766
767
        foreach ($keys as $value) {
768
            if (!$this->offsetExists($value)) {
769
                return false;
770
            }
771
        }
772
773
        return true;
774
    }
775
776
    /**
777
     * Concatenate values of a given key as a string.
778
     *
779
     * @param string $value
780
     * @param string $glue
781
     *
782
     * @return string
783
     */
784
    public function implode($value, $glue = null)
785
    {
786
        $first = $this->first();
787
788
        if (is_array($first) || is_object($first)) {
789
            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

789
            return implode(/** @scrutinizer ignore-type */ $glue, $this->pluck($value)->all());
Loading history...
790
        }
791
792
        return implode($value, $this->items);
793
    }
794
795
    /**
796
     * Intersect the collection with the given items.
797
     *
798
     * @param mixed $items
799
     *
800
     * @return static
801
     */
802
    public function intersect($items)
803
    {
804
        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
805
    }
806
807
    /**
808
     * Intersect the collection with the given items by key.
809
     *
810
     * @param mixed $items
811
     *
812
     * @return static
813
     */
814
    public function intersectByKeys($items)
815
    {
816
        return new static(array_intersect_key(
817
            $this->items,
818
            $this->getArrayableItems($items)
819
        ));
820
    }
821
822
    /**
823
     * Determine if the collection is empty or not.
824
     *
825
     * @return bool
826
     */
827
    public function isEmpty()
828
    {
829
        return empty($this->items);
830
    }
831
832
    /**
833
     * Determine if the collection is not empty.
834
     *
835
     * @return bool
836
     */
837
    public function isNotEmpty()
838
    {
839
        return !$this->isEmpty();
840
    }
841
842
    /**
843
     * Determine if the given value is callable, but not a string.
844
     *
845
     * @param mixed $value
846
     *
847
     * @return bool
848
     */
849
    protected function useAsCallable($value)
850
    {
851
        return !is_string($value) && is_callable($value);
852
    }
853
854
    /**
855
     * Get the keys of the collection items.
856
     *
857
     * @return static
858
     */
859
    public function keys()
860
    {
861
        return new static(array_keys($this->items));
862
    }
863
864
    /**
865
     * Get the last item from the collection.
866
     *
867
     * @param callable|null $callback
868
     * @param mixed         $default
869
     *
870
     * @return mixed
871
     */
872
    public function last(callable $callback = null, $default = null)
873
    {
874
        return Arr::last($this->items, $callback, $default);
875
    }
876
877
    /**
878
     * Get the values of a given key.
879
     *
880
     * @param string|array $value
881
     * @param string|null  $key
882
     *
883
     * @return static
884
     */
885
    public function pluck($value, $key = null)
886
    {
887
        return new static(Arr::pluck($this->items, $value, $key));
888
    }
889
890
    /**
891
     * Run a map over each of the items.
892
     *
893
     * @param callable $callback
894
     *
895
     * @return static
896
     */
897
    public function map(callable $callback)
898
    {
899
        $keys = array_keys($this->items);
900
901
        $items = array_map($callback, $this->items, $keys);
902
903
        return new static(array_combine($keys, $items));
904
    }
905
906
    /**
907
     * Run a map over each nested chunk of items.
908
     *
909
     * @param callable $callback
910
     *
911
     * @return static
912
     */
913
    public function mapSpread(callable $callback)
914
    {
915
        return $this->map(function ($chunk, $key) use ($callback) {
916
            $chunk[] = $key;
917
918
            return $callback(...$chunk);
919
        });
920
    }
921
922
    /**
923
     * Run a dictionary map over the items.
924
     *
925
     * The callback should return an associative array with a single key/value pair.
926
     *
927
     * @param callable $callback
928
     *
929
     * @return static
930
     */
931
    public function mapToDictionary(callable $callback)
932
    {
933
        $dictionary = $this->map($callback)->reduce(function ($groups, $pair) {
934
            $groups[key($pair)][] = reset($pair);
935
936
            return $groups;
937
        }, []);
938
939
        return new static($dictionary);
940
    }
941
942
    /**
943
     * Run a grouping map over the items.
944
     *
945
     * The callback should return an associative array with a single key/value pair.
946
     *
947
     * @param callable $callback
948
     *
949
     * @return static
950
     */
951
    public function mapToGroups(callable $callback)
952
    {
953
        $groups = $this->mapToDictionary($callback);
954
955
        return $groups->map([$this, 'make']);
956
    }
957
958
    /**
959
     * Run an associative map over each of the items.
960
     *
961
     * The callback should return an associative array with a single key/value pair.
962
     *
963
     * @param callable $callback
964
     *
965
     * @return static
966
     */
967
    public function mapWithKeys(callable $callback)
968
    {
969
        $result = [];
970
971
        foreach ($this->items as $key => $value) {
972
            $assoc = $callback($value, $key);
973
974
            foreach ($assoc as $mapKey => $mapValue) {
975
                $result[$mapKey] = $mapValue;
976
            }
977
        }
978
979
        return new static($result);
980
    }
981
982
    /**
983
     * Map a collection and flatten the result by a single level.
984
     *
985
     * @param callable $callback
986
     *
987
     * @return static
988
     */
989
    public function flatMap(callable $callback)
990
    {
991
        return $this->map($callback)->collapse();
992
    }
993
994
    /**
995
     * Map the values into a new class.
996
     *
997
     * @param string $class
998
     *
999
     * @return static
1000
     */
1001
    public function mapInto($class)
1002
    {
1003
        return $this->map(function ($value, $key) use ($class) {
1004
            return new $class($value, $key);
1005
        });
1006
    }
1007
1008
    /**
1009
     * Get the max value of a given key.
1010
     *
1011
     * @param callable|string|null $callback
1012
     *
1013
     * @return mixed
1014
     */
1015
    public function max($callback = null)
1016
    {
1017
        $callback = $this->valueRetriever($callback);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

1017
        $callback = $this->valueRetriever(/** @scrutinizer ignore-type */ $callback);
Loading history...
1018
1019
        return $this->filter(function ($value) {
1020
            return !is_null($value);
1021
        })->reduce(function ($result, $item) use ($callback) {
1022
            $value = $callback($item);
1023
1024
            return is_null($result) || $value > $result ? $value : $result;
1025
        });
1026
    }
1027
1028
    /**
1029
     * Merge the collection with the given items.
1030
     *
1031
     * @param mixed $items
1032
     *
1033
     * @return static
1034
     */
1035
    public function merge($items)
1036
    {
1037
        return new static(array_merge($this->items, $this->getArrayableItems($items)));
1038
    }
1039
1040
    /**
1041
     * Create a collection by using this collection for keys and another for its values.
1042
     *
1043
     * @param mixed $values
1044
     *
1045
     * @return static
1046
     */
1047
    public function combine($values)
1048
    {
1049
        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
1050
    }
1051
1052
    /**
1053
     * Union the collection with the given items.
1054
     *
1055
     * @param mixed $items
1056
     *
1057
     * @return static
1058
     */
1059
    public function union($items)
1060
    {
1061
        return new static($this->items + $this->getArrayableItems($items));
1062
    }
1063
1064
    /**
1065
     * Get the min value of a given key.
1066
     *
1067
     * @param callable|string|null $callback
1068
     *
1069
     * @return mixed
1070
     */
1071
    public function min($callback = null)
1072
    {
1073
        $callback = $this->valueRetriever($callback);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

1073
        $callback = $this->valueRetriever(/** @scrutinizer ignore-type */ $callback);
Loading history...
1074
1075
        return $this->filter(function ($value) {
1076
            return !is_null($value);
1077
        })->reduce(function ($result, $item) use ($callback) {
1078
            $value = $callback($item);
1079
1080
            return is_null($result) || $value < $result ? $value : $result;
1081
        });
1082
    }
1083
1084
    /**
1085
     * Create a new collection consisting of every n-th element.
1086
     *
1087
     * @param int $step
1088
     * @param int $offset
1089
     *
1090
     * @return static
1091
     */
1092
    public function nth($step, $offset = 0)
1093
    {
1094
        $new = [];
1095
1096
        $position = 0;
1097
1098
        foreach ($this->items as $item) {
1099
            if ($position % $step === $offset) {
1100
                $new[] = $item;
1101
            }
1102
1103
            $position++;
1104
        }
1105
1106
        return new static($new);
1107
    }
1108
1109
    /**
1110
     * Get the items with the specified keys.
1111
     *
1112
     * @param mixed $keys
1113
     *
1114
     * @return static
1115
     */
1116
    public function only($keys)
1117
    {
1118
        if (is_null($keys)) {
1119
            return new static($this->items);
1120
        }
1121
1122
        if ($keys instanceof self) {
1123
            $keys = $keys->all();
1124
        }
1125
1126
        $keys = is_array($keys) ? $keys : func_get_args();
1127
1128
        return new static(Arr::only($this->items, $keys));
1129
    }
1130
1131
    /**
1132
     * "Paginate" the collection by slicing it into a smaller collection.
1133
     *
1134
     * @param int $page
1135
     * @param int $perPage
1136
     *
1137
     * @return static
1138
     */
1139
    public function forPage($page, $perPage)
1140
    {
1141
        $offset = max(0, ($page - 1) * $perPage);
1142
1143
        return $this->slice($offset, $perPage);
1144
    }
1145
1146
    /**
1147
     * Partition the collection into two arrays using the given callback or key.
1148
     *
1149
     * @param callable|string $key
1150
     * @param mixed           $operator
1151
     * @param mixed           $value
1152
     *
1153
     * @return static
1154
     */
1155
    public function partition($key, $operator = null, $value = null)
1156
    {
1157
        $partitions = [new static(), new static()];
1158
        $callback = func_num_args() === 1
1159
            ? $this->valueRetriever($key)
1160
            : $this->operatorForWhere(...func_get_args());
1161
        foreach ($this->items as $key => $item) {
0 ignored issues
show
introduced by
$key is overwriting one of the parameters of this function.
Loading history...
1162
            $partitions[(int) !$callback($item, $key)][$key] = $item;
1163
        }
1164
1165
        return new static($partitions);
1166
    }
1167
1168
    /**
1169
     * Pass the collection to the given callback and return the result.
1170
     *
1171
     * @param callable $callback
1172
     *
1173
     * @return mixed
1174
     */
1175
    public function pipe(callable $callback)
1176
    {
1177
        return $callback($this);
1178
    }
1179
1180
    /**
1181
     * Get and remove the last item from the collection.
1182
     *
1183
     * @return mixed
1184
     */
1185
    public function pop()
1186
    {
1187
        return array_pop($this->items);
1188
    }
1189
1190
    /**
1191
     * Push an item onto the beginning of the collection.
1192
     *
1193
     * @param mixed $value
1194
     * @param mixed $key
1195
     *
1196
     * @return $this
1197
     */
1198
    public function prepend($value, $key = null)
1199
    {
1200
        $this->items = Arr::prepend($this->items, $value, $key);
1201
1202
        return $this;
1203
    }
1204
1205
    /**
1206
     * Push an item onto the end of the collection.
1207
     *
1208
     * @param mixed $value
1209
     *
1210
     * @return $this
1211
     */
1212
    public function push($value)
1213
    {
1214
        $this->offsetSet(null, $value);
1215
1216
        return $this;
1217
    }
1218
1219
    /**
1220
     * Push all of the given items onto the collection.
1221
     *
1222
     * @param \Traversable $source
1223
     *
1224
     * @return $this
1225
     */
1226
    public function concat($source)
1227
    {
1228
        $result = new static($this);
1229
1230
        foreach ($source as $item) {
1231
            $result->push($item);
1232
        }
1233
1234
        return $result;
1235
    }
1236
1237
    /**
1238
     * Get and remove an item from the collection.
1239
     *
1240
     * @param mixed $key
1241
     * @param mixed $default
1242
     *
1243
     * @return mixed
1244
     */
1245
    public function pull($key, $default = null)
1246
    {
1247
        return Arr::pull($this->items, $key, $default);
1248
    }
1249
1250
    /**
1251
     * Put an item in the collection by key.
1252
     *
1253
     * @param mixed $key
1254
     * @param mixed $value
1255
     *
1256
     * @return $this
1257
     */
1258
    public function put($key, $value)
1259
    {
1260
        $this->offsetSet($key, $value);
1261
1262
        return $this;
1263
    }
1264
1265
    /**
1266
     * Get one or a specified number of items randomly from the collection.
1267
     *
1268
     * @param int|null $number
1269
     *
1270
     * @throws \InvalidArgumentException
1271
     *
1272
     * @return mixed
1273
     */
1274
    public function random($number = null)
1275
    {
1276
        if (is_null($number)) {
1277
            return Arr::random($this->items);
1278
        }
1279
1280
        return new static(Arr::random($this->items, $number));
1281
    }
1282
1283
    /**
1284
     * Reduce the collection to a single value.
1285
     *
1286
     * @param callable $callback
1287
     * @param mixed    $initial
1288
     *
1289
     * @return mixed
1290
     */
1291
    public function reduce(callable $callback, $initial = null)
1292
    {
1293
        return array_reduce($this->items, $callback, $initial);
1294
    }
1295
1296
    /**
1297
     * Create a collection of all elements that do not pass a given truth test.
1298
     *
1299
     * @param callable|mixed $callback
1300
     *
1301
     * @return static
1302
     */
1303
    public function reject($callback)
1304
    {
1305
        if ($this->useAsCallable($callback)) {
1306
            return $this->filter(function ($value, $key) use ($callback) {
1307
                return !$callback($value, $key);
1308
            });
1309
        }
1310
1311
        return $this->filter(function ($item) use ($callback) {
1312
            return $item != $callback;
1313
        });
1314
    }
1315
1316
    /**
1317
     * Reverse items order.
1318
     *
1319
     * @return static
1320
     */
1321
    public function reverse()
1322
    {
1323
        return new static(array_reverse($this->items, true));
1324
    }
1325
1326
    /**
1327
     * Search the collection for a given value and return the corresponding key if successful.
1328
     *
1329
     * @param mixed $value
1330
     * @param bool  $strict
1331
     *
1332
     * @return mixed
1333
     */
1334
    public function search($value, $strict = false)
1335
    {
1336
        if (!$this->useAsCallable($value)) {
1337
            return array_search($value, $this->items, $strict);
1338
        }
1339
1340
        foreach ($this->items as $key => $item) {
1341
            if (call_user_func($value, $item, $key)) {
1342
                return $key;
1343
            }
1344
        }
1345
1346
        return false;
1347
    }
1348
1349
    /**
1350
     * Get and remove the first item from the collection.
1351
     *
1352
     * @return mixed
1353
     */
1354
    public function shift()
1355
    {
1356
        return array_shift($this->items);
1357
    }
1358
1359
    /**
1360
     * Shuffle the items in the collection.
1361
     *
1362
     * @param int $seed
1363
     *
1364
     * @return static
1365
     */
1366
    public function shuffle($seed = null)
1367
    {
1368
        $items = $this->items;
1369
1370
        if (is_null($seed)) {
1371
            shuffle($items);
1372
        } else {
1373
            srand($seed);
1374
1375
            usort($items, function () {
1376
                return rand(-1, 1);
1377
            });
1378
        }
1379
1380
        return new static($items);
1381
    }
1382
1383
    /**
1384
     * Slice the underlying collection array.
1385
     *
1386
     * @param int $offset
1387
     * @param int $length
1388
     *
1389
     * @return static
1390
     */
1391
    public function slice($offset, $length = null)
1392
    {
1393
        return new static(array_slice($this->items, $offset, $length, true));
1394
    }
1395
1396
    /**
1397
     * Split a collection into a certain number of groups.
1398
     *
1399
     * @param int $numberOfGroups
1400
     *
1401
     * @return static
1402
     */
1403
    public function split($numberOfGroups)
1404
    {
1405
        if ($this->isEmpty()) {
1406
            return new static();
1407
        }
1408
1409
        $groupSize = ceil($this->count() / $numberOfGroups);
1410
1411
        return $this->chunk($groupSize);
0 ignored issues
show
Bug introduced by
$groupSize of type double is incompatible with the type integer expected by parameter $size of MiotApi\Util\Collection\Collection::chunk(). ( Ignorable by Annotation )

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

1411
        return $this->chunk(/** @scrutinizer ignore-type */ $groupSize);
Loading history...
1412
    }
1413
1414
    /**
1415
     * Chunk the underlying collection array.
1416
     *
1417
     * @param int $size
1418
     *
1419
     * @return static
1420
     */
1421
    public function chunk($size)
1422
    {
1423
        if ($size <= 0) {
1424
            return new static();
1425
        }
1426
1427
        $chunks = [];
1428
1429
        foreach (array_chunk($this->items, $size, true) as $chunk) {
1430
            $chunks[] = new static($chunk);
1431
        }
1432
1433
        return new static($chunks);
1434
    }
1435
1436
    /**
1437
     * Sort through each item with a callback.
1438
     *
1439
     * @param callable|null $callback
1440
     *
1441
     * @return static
1442
     */
1443
    public function sort(callable $callback = null)
1444
    {
1445
        $items = $this->items;
1446
1447
        $callback
1448
            ? uasort($items, $callback)
1449
            : asort($items);
1450
1451
        return new static($items);
1452
    }
1453
1454
    /**
1455
     * Sort the collection using the given callback.
1456
     *
1457
     * @param callable|string $callback
1458
     * @param int             $options
1459
     * @param bool            $descending
1460
     *
1461
     * @return static
1462
     */
1463
    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
1464
    {
1465
        $results = [];
1466
1467
        $callback = $this->valueRetriever($callback);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

1467
        $callback = $this->valueRetriever(/** @scrutinizer ignore-type */ $callback);
Loading history...
1468
1469
        // First we will loop through the items and get the comparator from a callback
1470
        // function which we were given. Then, we will sort the returned values and
1471
        // and grab the corresponding values for the sorted keys from this array.
1472
        foreach ($this->items as $key => $value) {
1473
            $results[$key] = $callback($value, $key);
1474
        }
1475
1476
        $descending ? arsort($results, $options)
1477
            : asort($results, $options);
1478
1479
        // Once we have sorted all of the keys in the array, we will loop through them
1480
        // and grab the corresponding model so we can set the underlying items list
1481
        // to the sorted version. Then we'll just return the collection instance.
1482
        foreach (array_keys($results) as $key) {
1483
            $results[$key] = $this->items[$key];
1484
        }
1485
1486
        return new static($results);
1487
    }
1488
1489
    /**
1490
     * Sort the collection in descending order using the given callback.
1491
     *
1492
     * @param callable|string $callback
1493
     * @param int             $options
1494
     *
1495
     * @return static
1496
     */
1497
    public function sortByDesc($callback, $options = SORT_REGULAR)
1498
    {
1499
        return $this->sortBy($callback, $options, true);
1500
    }
1501
1502
    /**
1503
     * Splice a portion of the underlying collection array.
1504
     *
1505
     * @param int      $offset
1506
     * @param int|null $length
1507
     * @param mixed    $replacement
1508
     *
1509
     * @return static
1510
     */
1511
    public function splice($offset, $length = null, $replacement = [])
1512
    {
1513
        if (func_num_args() == 1) {
1514
            return new static(array_splice($this->items, $offset));
1515
        }
1516
1517
        return new static(array_splice($this->items, $offset, $length, $replacement));
1518
    }
1519
1520
    /**
1521
     * Get the sum of the given values.
1522
     *
1523
     * @param callable|string|null $callback
1524
     *
1525
     * @return mixed
1526
     */
1527
    public function sum($callback = null)
1528
    {
1529
        if (is_null($callback)) {
1530
            return array_sum($this->items);
1531
        }
1532
1533
        $callback = $this->valueRetriever($callback);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

1533
        $callback = $this->valueRetriever(/** @scrutinizer ignore-type */ $callback);
Loading history...
1534
1535
        return $this->reduce(function ($result, $item) use ($callback) {
1536
            return $result + $callback($item);
1537
        }, 0);
1538
    }
1539
1540
    /**
1541
     * Take the first or last {$limit} items.
1542
     *
1543
     * @param int $limit
1544
     *
1545
     * @return static
1546
     */
1547
    public function take($limit)
1548
    {
1549
        if ($limit < 0) {
1550
            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 MiotApi\Util\Collection\Collection::slice() 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

1550
            return $this->slice($limit, /** @scrutinizer ignore-type */ abs($limit));
Loading history...
1551
        }
1552
1553
        return $this->slice(0, $limit);
1554
    }
1555
1556
    /**
1557
     * Pass the collection to the given callback and then return it.
1558
     *
1559
     * @param callable $callback
1560
     *
1561
     * @return $this
1562
     */
1563
    public function tap(callable $callback)
1564
    {
1565
        $callback(new static($this->items));
1566
1567
        return $this;
1568
    }
1569
1570
    /**
1571
     * Transform each item in the collection using a callback.
1572
     *
1573
     * @param callable $callback
1574
     *
1575
     * @return $this
1576
     */
1577
    public function transform(callable $callback)
1578
    {
1579
        $this->items = $this->map($callback)->all();
1580
1581
        return $this;
1582
    }
1583
1584
    /**
1585
     * Return only unique items from the collection array.
1586
     *
1587
     * @param string|callable|null $key
1588
     * @param bool                 $strict
1589
     *
1590
     * @return static
1591
     */
1592
    public function unique($key = null, $strict = false)
1593
    {
1594
        if (is_null($key)) {
1595
            return new static(array_unique($this->items, SORT_REGULAR));
1596
        }
1597
1598
        $callback = $this->valueRetriever($key);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type callable; however, parameter $value of MiotApi\Util\Collection\...ction::valueRetriever() 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

1598
        $callback = $this->valueRetriever(/** @scrutinizer ignore-type */ $key);
Loading history...
1599
1600
        $exists = [];
1601
1602
        return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
1603
            if (in_array($id = $callback($item, $key), $exists, $strict)) {
1604
                return true;
1605
            }
1606
1607
            $exists[] = $id;
1608
        });
1609
    }
1610
1611
    /**
1612
     * Return only unique items from the collection array using strict comparison.
1613
     *
1614
     * @param string|callable|null $key
1615
     *
1616
     * @return static
1617
     */
1618
    public function uniqueStrict($key = null)
1619
    {
1620
        return $this->unique($key, true);
1621
    }
1622
1623
    /**
1624
     * Reset the keys on the underlying array.
1625
     *
1626
     * @return static
1627
     */
1628
    public function values()
1629
    {
1630
        return new static(array_values($this->items));
1631
    }
1632
1633
    /**
1634
     * Get a value retrieving callback.
1635
     *
1636
     * @param string $value
1637
     *
1638
     * @return callable
1639
     */
1640
    protected function valueRetriever($value)
1641
    {
1642
        if ($this->useAsCallable($value)) {
1643
            return $value;
1644
        }
1645
1646
        return function ($item) use ($value) {
1647
            return Arr::data_get($item, $value);
1648
        };
1649
    }
1650
1651
    /**
1652
     * Zip the collection together with one or more arrays.
1653
     *
1654
     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1655
     *      => [[1, 4], [2, 5], [3, 6]]
1656
     *
1657
     * @param mixed ...$items
1658
     *
1659
     * @return static
1660
     */
1661
    public function zip($items)
1662
    {
1663
        $arrayableItems = array_map(function ($items) {
1664
            return $this->getArrayableItems($items);
1665
        }, func_get_args());
1666
1667
        $params = array_merge([function () {
1668
            return new static(func_get_args());
1669
        }, $this->items], $arrayableItems);
1670
1671
        return new static(call_user_func_array('array_map', $params));
1672
    }
1673
1674
    /**
1675
     * Pad collection to the specified length with a value.
1676
     *
1677
     * @param int   $size
1678
     * @param mixed $value
1679
     *
1680
     * @return static
1681
     */
1682
    public function pad($size, $value)
1683
    {
1684
        return new static(array_pad($this->items, $size, $value));
1685
    }
1686
1687
    /**
1688
     * Get the collection of items as a plain array.
1689
     *
1690
     * @return array
1691
     */
1692
    public function toArray()
1693
    {
1694
        return array_map(function ($value) {
1695
            return $value instanceof Arrayable ? $value->toArray() : $value;
1696
        }, $this->items);
1697
    }
1698
1699
    /**
1700
     * Convert the object into something JSON serializable.
1701
     *
1702
     * @return array
1703
     */
1704
    public function jsonSerialize()
1705
    {
1706
        return array_map(function ($value) {
1707
            if ($value instanceof JsonSerializable) {
1708
                return $value->jsonSerialize();
1709
            } elseif ($value instanceof Jsonable) {
1710
                return json_decode($value->toJson(), true);
1711
            } elseif ($value instanceof Arrayable) {
1712
                return $value->toArray();
1713
            }
1714
1715
            return $value;
1716
        }, $this->items);
1717
    }
1718
1719
    /**
1720
     * Get the collection of items as JSON.
1721
     *
1722
     * @param int $options
1723
     *
1724
     * @return string
1725
     */
1726
    public function toJson($options = 0)
1727
    {
1728
        return json_encode($this->jsonSerialize(), $options);
1729
    }
1730
1731
    /**
1732
     * Get an iterator for the items.
1733
     *
1734
     * @return \ArrayIterator
1735
     */
1736
    public function getIterator()
1737
    {
1738
        return new ArrayIterator($this->items);
1739
    }
1740
1741
    /**
1742
     * Get a CachingIterator instance.
1743
     *
1744
     * @param int $flags
1745
     *
1746
     * @return \CachingIterator
1747
     */
1748
    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1749
    {
1750
        return new CachingIterator($this->getIterator(), $flags);
1751
    }
1752
1753
    /**
1754
     * Count the number of items in the collection.
1755
     *
1756
     * @return int
1757
     */
1758
    public function count()
1759
    {
1760
        return count($this->items);
1761
    }
1762
1763
    /**
1764
     * Get a base Support collection instance from this collection.
1765
     *
1766
     * @return Collection
1767
     */
1768
    public function toBase()
1769
    {
1770
        return new self($this);
1771
    }
1772
1773
    /**
1774
     * Determine if an item exists at an offset.
1775
     *
1776
     * @param mixed $key
1777
     *
1778
     * @return bool
1779
     */
1780
    public function offsetExists($key)
1781
    {
1782
        return array_key_exists($key, $this->items);
1783
    }
1784
1785
    /**
1786
     * Get an item at a given offset.
1787
     *
1788
     * @param mixed $key
1789
     *
1790
     * @return mixed
1791
     */
1792
    public function offsetGet($key)
1793
    {
1794
        return $this->items[$key];
1795
    }
1796
1797
    /**
1798
     * Set the item at a given offset.
1799
     *
1800
     * @param mixed $key
1801
     * @param mixed $value
1802
     *
1803
     * @return void
1804
     */
1805
    public function offsetSet($key, $value)
1806
    {
1807
        if (is_null($key)) {
1808
            $this->items[] = $value;
1809
        } else {
1810
            $this->items[$key] = $value;
1811
        }
1812
    }
1813
1814
    /**
1815
     * Unset the item at a given offset.
1816
     *
1817
     * @param string $key
1818
     *
1819
     * @return void
1820
     */
1821
    public function offsetUnset($key)
1822
    {
1823
        unset($this->items[$key]);
1824
    }
1825
1826
    /**
1827
     * Convert the collection to its string representation.
1828
     *
1829
     * @return string
1830
     */
1831
    public function __toString()
1832
    {
1833
        return $this->toJson();
1834
    }
1835
1836
    /**
1837
     * Results array of items from Collection or Arrayable.
1838
     *
1839
     * @param mixed $items
1840
     *
1841
     * @return array
1842
     */
1843
    protected function getArrayableItems($items)
1844
    {
1845
        if (is_array($items)) {
1846
            return $items;
1847
        } elseif ($items instanceof self) {
1848
            return $items->all();
1849
        } elseif ($items instanceof Arrayable) {
1850
            return $items->toArray();
1851
        } elseif ($items instanceof Jsonable) {
1852
            return json_decode($items->toJson(), true);
1853
        } elseif ($items instanceof JsonSerializable) {
1854
            return $items->jsonSerialize();
1855
        } elseif ($items instanceof Traversable) {
1856
            return iterator_to_array($items);
1857
        }
1858
1859
        return (array) $items;
1860
    }
1861
1862
    /**
1863
     * Add a method to the list of proxied methods.
1864
     *
1865
     * @param string $method
1866
     *
1867
     * @return void
1868
     */
1869
    public static function proxy($method)
1870
    {
1871
        static::$proxies[] = $method;
1872
    }
1873
1874
    /**
1875
     * Dynamically access collection proxies.
1876
     *
1877
     * @param string $key
1878
     *
1879
     * @throws \Exception
1880
     *
1881
     * @return mixed
1882
     */
1883
    public function __get($key)
1884
    {
1885
        if (!in_array($key, static::$proxies)) {
1886
            throw new Exception("Property [{$key}] does not exist on this collection instance.");
1887
        }
1888
1889
        return new HigherOrderCollectionProxy($this, $key);
1890
    }
1891
}
1892