1<?php
2
3namespace Illuminate\Support;
4
5use Countable;
6use ArrayAccess;
7use Traversable;
8use ArrayIterator;
9use CachingIterator;
10use JsonSerializable;
11use IteratorAggregate;
12use InvalidArgumentException;
13use Illuminate\Support\Traits\Macroable;
14use Illuminate\Contracts\Support\Jsonable;
15use Illuminate\Contracts\Support\Arrayable;
16
17class Collection implements ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
18{
19    use Macroable;
20
21    /**
22     * The items contained in the collection.
23     *
24     * @var array
25     */
26    protected $items = [];
27
28    /**
29     * Create a new collection.
30     *
31     * @param  mixed  $items
32     * @return void
33     */
34    public function __construct($items = [])
35    {
36        $this->items = $this->getArrayableItems($items);
37    }
38
39    /**
40     * Create a new collection instance if the value isn't one already.
41     *
42     * @param  mixed  $items
43     * @return static
44     */
45    public static function make($items = [])
46    {
47        return new static($items);
48    }
49
50    /**
51     * Get all of the items in the collection.
52     *
53     * @return array
54     */
55    public function all()
56    {
57        return $this->items;
58    }
59
60    /**
61     * Get the average value of a given key.
62     *
63     * @param  string|null  $key
64     * @return mixed
65     */
66    public function avg($key = null)
67    {
68        if ($count = $this->count()) {
69            return $this->sum($key) / $count;
70        }
71    }
72
73    /**
74     * Alias for the "avg" method.
75     *
76     * @param  string|null  $key
77     * @return mixed
78     */
79    public function average($key = null)
80    {
81        return $this->avg($key);
82    }
83
84    /**
85     * Get the median of a given key.
86     *
87     * @param  null $key
88     * @return mixed|null
89     */
90    public function median($key = null)
91    {
92        $count = $this->count();
93
94        if ($count == 0) {
95            return;
96        }
97
98        $values = with(isset($key) ? $this->pluck($key) : $this)
99                    ->sort()->values();
100
101        $middle = (int) floor($count / 2);
102
103        if ($count % 2) {
104            return $values->get($middle);
105        }
106
107        return (new static([
108            $values->get($middle - 1), $values->get($middle),
109        ]))->average();
110    }
111
112    /**
113     * Get the mode of a given key.
114     *
115     * @param  null $key
116     * @return array
117     */
118    public function mode($key = null)
119    {
120        $count = $this->count();
121
122        if ($count == 0) {
123            return;
124        }
125
126        $collection = isset($key) ? $this->pluck($key) : $this;
127
128        $counts = new self;
129
130        $collection->each(function ($value) use ($counts) {
131            $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
132        });
133
134        $sorted = $counts->sort();
135
136        $highestValue = $sorted->last();
137
138        return $sorted->filter(function ($value) use ($highestValue) {
139            return $value == $highestValue;
140        })->sort()->keys()->all();
141    }
142
143    /**
144     * Collapse the collection of items into a single array.
145     *
146     * @return static
147     */
148    public function collapse()
149    {
150        return new static(Arr::collapse($this->items));
151    }
152
153    /**
154     * Determine if an item exists in the collection.
155     *
156     * @param  mixed  $key
157     * @param  mixed  $value
158     * @return bool
159     */
160    public function contains($key, $value = null)
161    {
162        if (func_num_args() == 2) {
163            return $this->contains(function ($k, $item) use ($key, $value) {
164                return data_get($item, $key) == $value;
165            });
166        }
167
168        if ($this->useAsCallable($key)) {
169            return ! is_null($this->first($key));
170        }
171
172        return in_array($key, $this->items);
173    }
174
175    /**
176     * Get the items in the collection that are not present in the given items.
177     *
178     * @param  mixed  $items
179     * @return static
180     */
181    public function diff($items)
182    {
183        return new static(array_diff($this->items, $this->getArrayableItems($items)));
184    }
185
186    /**
187     * Get the items in the collection whose keys are not present in the given items.
188     *
189     * @param  mixed  $items
190     * @return static
191     */
192    public function diffKeys($items)
193    {
194        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
195    }
196
197    /**
198     * Execute a callback over each item.
199     *
200     * @param  callable  $callback
201     * @return $this
202     */
203    public function each(callable $callback)
204    {
205        foreach ($this->items as $key => $item) {
206            if ($callback($item, $key) === false) {
207                break;
208            }
209        }
210
211        return $this;
212    }
213
214    /**
215     * Create a new collection consisting of every n-th element.
216     *
217     * @param  int  $step
218     * @param  int  $offset
219     * @return static
220     */
221    public function every($step, $offset = 0)
222    {
223        $new = [];
224
225        $position = 0;
226
227        foreach ($this->items as $item) {
228            if ($position % $step === $offset) {
229                $new[] = $item;
230            }
231
232            $position++;
233        }
234
235        return new static($new);
236    }
237
238    /**
239     * Get all items except for those with the specified keys.
240     *
241     * @param  mixed  $keys
242     * @return static
243     */
244    public function except($keys)
245    {
246        $keys = is_array($keys) ? $keys : func_get_args();
247
248        return new static(Arr::except($this->items, $keys));
249    }
250
251    /**
252     * Run a filter over each of the items.
253     *
254     * @param  callable|null  $callback
255     * @return static
256     */
257    public function filter(callable $callback = null)
258    {
259        if ($callback) {
260            $return = [];
261
262            foreach ($this->items as $key => $value) {
263                if ($callback($value, $key)) {
264                    $return[$key] = $value;
265                }
266            }
267
268            return new static($return);
269        }
270
271        return new static(array_filter($this->items));
272    }
273
274    /**
275     * Filter items by the given key value pair.
276     *
277     * @param  string  $key
278     * @param  mixed  $value
279     * @param  bool  $strict
280     * @return static
281     */
282    public function where($key, $value, $strict = true)
283    {
284        return $this->filter(function ($item) use ($key, $value, $strict) {
285            return $strict ? data_get($item, $key) === $value
286                           : data_get($item, $key) == $value;
287        });
288    }
289
290    /**
291     * Filter items by the given key value pair using loose comparison.
292     *
293     * @param  string  $key
294     * @param  mixed  $value
295     * @return static
296     */
297    public function whereLoose($key, $value)
298    {
299        return $this->where($key, $value, false);
300    }
301
302    /**
303     * Filter items by the given key value pair.
304     *
305     * @param  string  $key
306     * @param  array  $values
307     * @param  bool  $strict
308     * @return static
309     */
310    public function whereIn($key, array $values, $strict = true)
311    {
312        return $this->filter(function ($item) use ($key, $values, $strict) {
313            return in_array(data_get($item, $key), $values, $strict);
314        });
315    }
316
317    /**
318     * Filter items by the given key value pair using loose comparison.
319     *
320     * @param  string  $key
321     * @param  array  $values
322     * @return static
323     */
324    public function whereInLoose($key, array $values)
325    {
326        return $this->whereIn($key, $values, false);
327    }
328
329    /**
330     * Get the first item from the collection.
331     *
332     * @param  callable|null  $callback
333     * @param  mixed  $default
334     * @return mixed
335     */
336    public function first(callable $callback = null, $default = null)
337    {
338        return Arr::first($this->items, $callback, $default);
339    }
340
341    /**
342     * Get a flattened array of the items in the collection.
343     *
344     * @param  int  $depth
345     * @return static
346     */
347    public function flatten($depth = INF)
348    {
349        return new static(Arr::flatten($this->items, $depth));
350    }
351
352    /**
353     * Flip the items in the collection.
354     *
355     * @return static
356     */
357    public function flip()
358    {
359        return new static(array_flip($this->items));
360    }
361
362    /**
363     * Remove an item from the collection by key.
364     *
365     * @param  string|array  $keys
366     * @return $this
367     */
368    public function forget($keys)
369    {
370        foreach ((array) $keys as $key) {
371            $this->offsetUnset($key);
372        }
373
374        return $this;
375    }
376
377    /**
378     * Get an item from the collection by key.
379     *
380     * @param  mixed  $key
381     * @param  mixed  $default
382     * @return mixed
383     */
384    public function get($key, $default = null)
385    {
386        if ($this->offsetExists($key)) {
387            return $this->items[$key];
388        }
389
390        return value($default);
391    }
392
393    /**
394     * Group an associative array by a field or using a callback.
395     *
396     * @param  callable|string  $groupBy
397     * @param  bool  $preserveKeys
398     * @return static
399     */
400    public function groupBy($groupBy, $preserveKeys = false)
401    {
402        $groupBy = $this->valueRetriever($groupBy);
403
404        $results = [];
405
406        foreach ($this->items as $key => $value) {
407            $groupKeys = $groupBy($value, $key);
408
409            if (! is_array($groupKeys)) {
410                $groupKeys = [$groupKeys];
411            }
412
413            foreach ($groupKeys as $groupKey) {
414                if (! array_key_exists($groupKey, $results)) {
415                    $results[$groupKey] = new static;
416                }
417
418                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
419            }
420        }
421
422        return new static($results);
423    }
424
425    /**
426     * Key an associative array by a field or using a callback.
427     *
428     * @param  callable|string  $keyBy
429     * @return static
430     */
431    public function keyBy($keyBy)
432    {
433        $keyBy = $this->valueRetriever($keyBy);
434
435        $results = [];
436
437        foreach ($this->items as $key => $item) {
438            $results[$keyBy($item, $key)] = $item;
439        }
440
441        return new static($results);
442    }
443
444    /**
445     * Determine if an item exists in the collection by key.
446     *
447     * @param  mixed  $key
448     * @return bool
449     */
450    public function has($key)
451    {
452        return $this->offsetExists($key);
453    }
454
455    /**
456     * Concatenate values of a given key as a string.
457     *
458     * @param  string  $value
459     * @param  string  $glue
460     * @return string
461     */
462    public function implode($value, $glue = null)
463    {
464        $first = $this->first();
465
466        if (is_array($first) || is_object($first)) {
467            return implode($glue, $this->pluck($value)->all());
468        }
469
470        return implode($value, $this->items);
471    }
472
473    /**
474     * Intersect the collection with the given items.
475     *
476     * @param  mixed  $items
477     * @return static
478     */
479    public function intersect($items)
480    {
481        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
482    }
483
484    /**
485     * Determine if the collection is empty or not.
486     *
487     * @return bool
488     */
489    public function isEmpty()
490    {
491        return empty($this->items);
492    }
493
494    /**
495     * Determine if the given value is callable, but not a string.
496     *
497     * @param  mixed  $value
498     * @return bool
499     */
500    protected function useAsCallable($value)
501    {
502        return ! is_string($value) && is_callable($value);
503    }
504
505    /**
506     * Get the keys of the collection items.
507     *
508     * @return static
509     */
510    public function keys()
511    {
512        return new static(array_keys($this->items));
513    }
514
515    /**
516     * Get the last item from the collection.
517     *
518     * @param  callable|null  $callback
519     * @param  mixed  $default
520     * @return mixed
521     */
522    public function last(callable $callback = null, $default = null)
523    {
524        return Arr::last($this->items, $callback, $default);
525    }
526
527    /**
528     * Get the values of a given key.
529     *
530     * @param  string  $value
531     * @param  string|null  $key
532     * @return static
533     */
534    public function pluck($value, $key = null)
535    {
536        return new static(Arr::pluck($this->items, $value, $key));
537    }
538
539    /**
540     * Alias for the "pluck" method.
541     *
542     * @param  string  $value
543     * @param  string|null  $key
544     * @return static
545     *
546     * @deprecated since version 5.2. Use the "pluck" method directly.
547     */
548    public function lists($value, $key = null)
549    {
550        return $this->pluck($value, $key);
551    }
552
553    /**
554     * Run a map over each of the items.
555     *
556     * @param  callable  $callback
557     * @return static
558     */
559    public function map(callable $callback)
560    {
561        $keys = array_keys($this->items);
562
563        $items = array_map($callback, $this->items, $keys);
564
565        return new static(array_combine($keys, $items));
566    }
567
568    /**
569     * Map a collection and flatten the result by a single level.
570     *
571     * @param  callable  $callback
572     * @return static
573     */
574    public function flatMap(callable $callback)
575    {
576        return $this->map($callback)->collapse();
577    }
578
579    /**
580     * Get the max value of a given key.
581     *
582     * @param  string|null  $key
583     * @return mixed
584     */
585    public function max($key = null)
586    {
587        return $this->reduce(function ($result, $item) use ($key) {
588            $value = data_get($item, $key);
589
590            return is_null($result) || $value > $result ? $value : $result;
591        });
592    }
593
594    /**
595     * Merge the collection with the given items.
596     *
597     * @param  mixed  $items
598     * @return static
599     */
600    public function merge($items)
601    {
602        return new static(array_merge($this->items, $this->getArrayableItems($items)));
603    }
604
605    /**
606     * Create a collection by using this collection for keys and another for its values.
607     *
608     * @param  mixed  $values
609     * @return static
610     */
611    public function combine($values)
612    {
613        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
614    }
615
616    /**
617     * Union the collection with the given items.
618     *
619     * @param  mixed  $items
620     * @return static
621     */
622    public function union($items)
623    {
624        return new static($this->items + $this->getArrayableItems($items));
625    }
626
627    /**
628     * Get the min value of a given key.
629     *
630     * @param  string|null  $key
631     * @return mixed
632     */
633    public function min($key = null)
634    {
635        return $this->reduce(function ($result, $item) use ($key) {
636            $value = data_get($item, $key);
637
638            return is_null($result) || $value < $result ? $value : $result;
639        });
640    }
641
642    /**
643     * Get the items with the specified keys.
644     *
645     * @param  mixed  $keys
646     * @return static
647     */
648    public function only($keys)
649    {
650        $keys = is_array($keys) ? $keys : func_get_args();
651
652        return new static(Arr::only($this->items, $keys));
653    }
654
655    /**
656     * "Paginate" the collection by slicing it into a smaller collection.
657     *
658     * @param  int  $page
659     * @param  int  $perPage
660     * @return static
661     */
662    public function forPage($page, $perPage)
663    {
664        return $this->slice(($page - 1) * $perPage, $perPage);
665    }
666
667    /**
668     * Pass the collection to the given callback and return the result.
669     *
670     * @param  callable $callback
671     * @return mixed
672     */
673    public function pipe(callable $callback)
674    {
675        return $callback($this);
676    }
677
678    /**
679     * Get and remove the last item from the collection.
680     *
681     * @return mixed
682     */
683    public function pop()
684    {
685        return array_pop($this->items);
686    }
687
688    /**
689     * Push an item onto the beginning of the collection.
690     *
691     * @param  mixed  $value
692     * @param  mixed  $key
693     * @return $this
694     */
695    public function prepend($value, $key = null)
696    {
697        $this->items = Arr::prepend($this->items, $value, $key);
698
699        return $this;
700    }
701
702    /**
703     * Push an item onto the end of the collection.
704     *
705     * @param  mixed  $value
706     * @return $this
707     */
708    public function push($value)
709    {
710        $this->offsetSet(null, $value);
711
712        return $this;
713    }
714
715    /**
716     * Get and remove an item from the collection.
717     *
718     * @param  mixed  $key
719     * @param  mixed  $default
720     * @return mixed
721     */
722    public function pull($key, $default = null)
723    {
724        return Arr::pull($this->items, $key, $default);
725    }
726
727    /**
728     * Put an item in the collection by key.
729     *
730     * @param  mixed  $key
731     * @param  mixed  $value
732     * @return $this
733     */
734    public function put($key, $value)
735    {
736        $this->offsetSet($key, $value);
737
738        return $this;
739    }
740
741    /**
742     * Get one or more items randomly from the collection.
743     *
744     * @param  int  $amount
745     * @return mixed
746     *
747     * @throws \InvalidArgumentException
748     */
749    public function random($amount = 1)
750    {
751        if ($amount > ($count = $this->count())) {
752            throw new InvalidArgumentException("You requested {$amount} items, but there are only {$count} items in the collection");
753        }
754
755        $keys = array_rand($this->items, $amount);
756
757        if ($amount == 1) {
758            return $this->items[$keys];
759        }
760
761        return new static(array_intersect_key($this->items, array_flip($keys)));
762    }
763
764    /**
765     * Reduce the collection to a single value.
766     *
767     * @param  callable  $callback
768     * @param  mixed     $initial
769     * @return mixed
770     */
771    public function reduce(callable $callback, $initial = null)
772    {
773        return array_reduce($this->items, $callback, $initial);
774    }
775
776    /**
777     * Create a collection of all elements that do not pass a given truth test.
778     *
779     * @param  callable|mixed  $callback
780     * @return static
781     */
782    public function reject($callback)
783    {
784        if ($this->useAsCallable($callback)) {
785            return $this->filter(function ($value, $key) use ($callback) {
786                return ! $callback($value, $key);
787            });
788        }
789
790        return $this->filter(function ($item) use ($callback) {
791            return $item != $callback;
792        });
793    }
794
795    /**
796     * Reverse items order.
797     *
798     * @return static
799     */
800    public function reverse()
801    {
802        return new static(array_reverse($this->items, true));
803    }
804
805    /**
806     * Search the collection for a given value and return the corresponding key if successful.
807     *
808     * @param  mixed  $value
809     * @param  bool   $strict
810     * @return mixed
811     */
812    public function search($value, $strict = false)
813    {
814        if (! $this->useAsCallable($value)) {
815            return array_search($value, $this->items, $strict);
816        }
817
818        foreach ($this->items as $key => $item) {
819            if (call_user_func($value, $item, $key)) {
820                return $key;
821            }
822        }
823
824        return false;
825    }
826
827    /**
828     * Get and remove the first item from the collection.
829     *
830     * @return mixed
831     */
832    public function shift()
833    {
834        return array_shift($this->items);
835    }
836
837    /**
838     * Shuffle the items in the collection.
839     *
840     * @param int $seed
841     * @return static
842     */
843    public function shuffle($seed = null)
844    {
845        $items = $this->items;
846
847        if (is_null($seed)) {
848            shuffle($items);
849        } else {
850            srand($seed);
851
852            usort($items, function () {
853                return rand(-1, 1);
854            });
855        }
856
857        return new static($items);
858    }
859
860    /**
861     * Slice the underlying collection array.
862     *
863     * @param  int   $offset
864     * @param  int   $length
865     * @return static
866     */
867    public function slice($offset, $length = null)
868    {
869        return new static(array_slice($this->items, $offset, $length, true));
870    }
871
872    /**
873     * Chunk the underlying collection array.
874     *
875     * @param  int   $size
876     * @return static
877     */
878    public function chunk($size)
879    {
880        $chunks = [];
881
882        foreach (array_chunk($this->items, $size, true) as $chunk) {
883            $chunks[] = new static($chunk);
884        }
885
886        return new static($chunks);
887    }
888
889    /**
890     * Sort through each item with a callback.
891     *
892     * @param  callable|null  $callback
893     * @return static
894     */
895    public function sort(callable $callback = null)
896    {
897        $items = $this->items;
898
899        $callback ? uasort($items, $callback) : uasort($items, function ($a, $b) {
900            if ($a == $b) {
901                return 0;
902            }
903
904            return ($a < $b) ? -1 : 1;
905        });
906
907        return new static($items);
908    }
909
910    /**
911     * Sort the collection using the given callback.
912     *
913     * @param  callable|string  $callback
914     * @param  int   $options
915     * @param  bool  $descending
916     * @return static
917     */
918    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
919    {
920        $results = [];
921
922        $callback = $this->valueRetriever($callback);
923
924        // First we will loop through the items and get the comparator from a callback
925        // function which we were given. Then, we will sort the returned values and
926        // and grab the corresponding values for the sorted keys from this array.
927        foreach ($this->items as $key => $value) {
928            $results[$key] = $callback($value, $key);
929        }
930
931        $descending ? arsort($results, $options)
932                    : asort($results, $options);
933
934        // Once we have sorted all of the keys in the array, we will loop through them
935        // and grab the corresponding model so we can set the underlying items list
936        // to the sorted version. Then we'll just return the collection instance.
937        foreach (array_keys($results) as $key) {
938            $results[$key] = $this->items[$key];
939        }
940
941        return new static($results);
942    }
943
944    /**
945     * Sort the collection in descending order using the given callback.
946     *
947     * @param  callable|string  $callback
948     * @param  int  $options
949     * @return static
950     */
951    public function sortByDesc($callback, $options = SORT_REGULAR)
952    {
953        return $this->sortBy($callback, $options, true);
954    }
955
956    /**
957     * Splice a portion of the underlying collection array.
958     *
959     * @param  int  $offset
960     * @param  int|null  $length
961     * @param  mixed  $replacement
962     * @return static
963     */
964    public function splice($offset, $length = null, $replacement = [])
965    {
966        if (func_num_args() == 1) {
967            return new static(array_splice($this->items, $offset));
968        }
969
970        return new static(array_splice($this->items, $offset, $length, $replacement));
971    }
972
973    /**
974     * Get the sum of the given values.
975     *
976     * @param  callable|string|null  $callback
977     * @return mixed
978     */
979    public function sum($callback = null)
980    {
981        if (is_null($callback)) {
982            return array_sum($this->items);
983        }
984
985        $callback = $this->valueRetriever($callback);
986
987        return $this->reduce(function ($result, $item) use ($callback) {
988            return $result += $callback($item);
989        }, 0);
990    }
991
992    /**
993     * Take the first or last {$limit} items.
994     *
995     * @param  int  $limit
996     * @return static
997     */
998    public function take($limit)
999    {
1000        if ($limit < 0) {
1001            return $this->slice($limit, abs($limit));
1002        }
1003
1004        return $this->slice(0, $limit);
1005    }
1006
1007    /**
1008     * Transform each item in the collection using a callback.
1009     *
1010     * @param  callable  $callback
1011     * @return $this
1012     */
1013    public function transform(callable $callback)
1014    {
1015        $this->items = $this->map($callback)->all();
1016
1017        return $this;
1018    }
1019
1020    /**
1021     * Return only unique items from the collection array.
1022     *
1023     * @param  string|callable|null  $key
1024     * @return static
1025     */
1026    public function unique($key = null)
1027    {
1028        if (is_null($key)) {
1029            return new static(array_unique($this->items, SORT_REGULAR));
1030        }
1031
1032        $key = $this->valueRetriever($key);
1033
1034        $exists = [];
1035
1036        return $this->reject(function ($item) use ($key, &$exists) {
1037            if (in_array($id = $key($item), $exists)) {
1038                return true;
1039            }
1040
1041            $exists[] = $id;
1042        });
1043    }
1044
1045    /**
1046     * Reset the keys on the underlying array.
1047     *
1048     * @return static
1049     */
1050    public function values()
1051    {
1052        return new static(array_values($this->items));
1053    }
1054
1055    /**
1056     * Get a value retrieving callback.
1057     *
1058     * @param  string  $value
1059     * @return callable
1060     */
1061    protected function valueRetriever($value)
1062    {
1063        if ($this->useAsCallable($value)) {
1064            return $value;
1065        }
1066
1067        return function ($item) use ($value) {
1068            return data_get($item, $value);
1069        };
1070    }
1071
1072    /**
1073     * Zip the collection together with one or more arrays.
1074     *
1075     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1076     *      => [[1, 4], [2, 5], [3, 6]]
1077     *
1078     * @param  mixed ...$items
1079     * @return static
1080     */
1081    public function zip($items)
1082    {
1083        $arrayableItems = array_map(function ($items) {
1084            return $this->getArrayableItems($items);
1085        }, func_get_args());
1086
1087        $params = array_merge([function () {
1088            return new static(func_get_args());
1089        }, $this->items], $arrayableItems);
1090
1091        return new static(call_user_func_array('array_map', $params));
1092    }
1093
1094    /**
1095     * Get the collection of items as a plain array.
1096     *
1097     * @return array
1098     */
1099    public function toArray()
1100    {
1101        return array_map(function ($value) {
1102            return $value instanceof Arrayable ? $value->toArray() : $value;
1103        }, $this->items);
1104    }
1105
1106    /**
1107     * Convert the object into something JSON serializable.
1108     *
1109     * @return array
1110     */
1111    public function jsonSerialize()
1112    {
1113        return array_map(function ($value) {
1114            if ($value instanceof JsonSerializable) {
1115                return $value->jsonSerialize();
1116            } elseif ($value instanceof Jsonable) {
1117                return json_decode($value->toJson(), true);
1118            } elseif ($value instanceof Arrayable) {
1119                return $value->toArray();
1120            } else {
1121                return $value;
1122            }
1123        }, $this->items);
1124    }
1125
1126    /**
1127     * Get the collection of items as JSON.
1128     *
1129     * @param  int  $options
1130     * @return string
1131     */
1132    public function toJson($options = 0)
1133    {
1134        return json_encode($this->jsonSerialize(), $options);
1135    }
1136
1137    /**
1138     * Get an iterator for the items.
1139     *
1140     * @return \ArrayIterator
1141     */
1142    public function getIterator()
1143    {
1144        return new ArrayIterator($this->items);
1145    }
1146
1147    /**
1148     * Get a CachingIterator instance.
1149     *
1150     * @param  int  $flags
1151     * @return \CachingIterator
1152     */
1153    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1154    {
1155        return new CachingIterator($this->getIterator(), $flags);
1156    }
1157
1158    /**
1159     * Count the number of items in the collection.
1160     *
1161     * @return int
1162     */
1163    public function count()
1164    {
1165        return count($this->items);
1166    }
1167
1168    /**
1169     * Determine if an item exists at an offset.
1170     *
1171     * @param  mixed  $key
1172     * @return bool
1173     */
1174    public function offsetExists($key)
1175    {
1176        return array_key_exists($key, $this->items);
1177    }
1178
1179    /**
1180     * Get an item at a given offset.
1181     *
1182     * @param  mixed  $key
1183     * @return mixed
1184     */
1185    public function offsetGet($key)
1186    {
1187        return $this->items[$key];
1188    }
1189
1190    /**
1191     * Set the item at a given offset.
1192     *
1193     * @param  mixed  $key
1194     * @param  mixed  $value
1195     * @return void
1196     */
1197    public function offsetSet($key, $value)
1198    {
1199        if (is_null($key)) {
1200            $this->items[] = $value;
1201        } else {
1202            $this->items[$key] = $value;
1203        }
1204    }
1205
1206    /**
1207     * Unset the item at a given offset.
1208     *
1209     * @param  string  $key
1210     * @return void
1211     */
1212    public function offsetUnset($key)
1213    {
1214        unset($this->items[$key]);
1215    }
1216
1217    /**
1218     * Convert the collection to its string representation.
1219     *
1220     * @return string
1221     */
1222    public function __toString()
1223    {
1224        return $this->toJson();
1225    }
1226
1227    /**
1228     * Results array of items from Collection or Arrayable.
1229     *
1230     * @param  mixed  $items
1231     * @return array
1232     */
1233    protected function getArrayableItems($items)
1234    {
1235        if (is_array($items)) {
1236            return $items;
1237        } elseif ($items instanceof self) {
1238            return $items->all();
1239        } elseif ($items instanceof Arrayable) {
1240            return $items->toArray();
1241        } elseif ($items instanceof Jsonable) {
1242            return json_decode($items->toJson(), true);
1243        } elseif ($items instanceof JsonSerializable) {
1244            return $items->jsonSerialize();
1245        } elseif ($items instanceof Traversable) {
1246            return iterator_to_array($items);
1247        }
1248
1249        return (array) $items;
1250    }
1251}
1252