PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.97
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.97
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Support / Collection.php

Collection.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 1.0.97, at vendor/wpfluent/framework/src/WPFluent/Support/Collection.php

1,674 lines 40.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Framework\Support;
4
5 use stdClass;
6 use ArrayAccess;
7 use ArrayIterator;
8 use FluentCommunity\Framework\Support\Helper;
9 use FluentCommunity\Framework\Support\MacroableTrait;
10 use FluentCommunity\Framework\Support\EnumeratesValues;
11 use FluentCommunity\Framework\Support\CanBeEscapedWhenCastToString;
12
13 class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
14 {
15 use EnumeratesValues, MacroableTrait;
16
17 /**
18 * The items contained in the collection.
19 *
20 * @var array
21 */
22 protected $items = [];
23
24 /**
25 * Create a new collection.
26 *
27 * @param mixed $items
28 * @return void
29 */
30 public function __construct($items = [])
31 {
32 $this->items = $this->getArrayableItems($items);
33 }
34
35 /**
36 * Create a collection with the given range.
37 *
38 * @param int $from
39 * @param int $to
40 * @return static
41 */
42 public static function range($from, $to)
43 {
44 return new static(range($from, $to));
45 }
46
47 /**
48 * Get all of the items in the collection.
49 *
50 * @return array
51 */
52 public function all()
53 {
54 return $this->items;
55 }
56
57 /**
58 * Get a lazy collection for the items in this collection.
59 *
60 * @return \FluentCommunity\Framework\Support\LazyCollection
61 */
62 public function lazy()
63 {
64 return new LazyCollection($this->items);
65 }
66
67 /**
68 * Get the average value of a given key.
69 *
70 * @param callable|string|null $callback
71 * @return mixed
72 */
73 public function avg($callback = null)
74 {
75 $callback = $this->valueRetriever($callback);
76
77 $items = $this->map(function ($value) use ($callback) {
78 return $callback($value);
79 })->filter(function ($value) {
80 return ! is_null($value);
81 });
82
83 if ($count = $items->count()) {
84 return $items->sum() / $count;
85 }
86 }
87
88 /**
89 * Get the median of a given key.
90 *
91 * @param string|array|null $key
92 * @return mixed
93 */
94 public function median($key = null)
95 {
96 $values = (isset($key) ? $this->pluck($key) : $this)
97 ->filter(function ($item) {
98 return ! is_null($item);
99 })->sort()->values();
100
101 $count = $values->count();
102
103 if ($count === 0) {
104 return;
105 }
106
107 $middle = (int) ($count / 2);
108
109 if ($count % 2) {
110 return $values->get($middle);
111 }
112
113 return (new static([
114 $values->get($middle - 1), $values->get($middle),
115 ]))->average();
116 }
117
118 /**
119 * Get the mode of a given key.
120 *
121 * @param string|array|null $key
122 * @return array|null
123 */
124 public function mode($key = null)
125 {
126 if ($this->count() === 0) {
127 return;
128 }
129
130 $collection = isset($key) ? $this->pluck($key) : $this;
131
132 $counts = new static;
133
134 $collection->each(function ($value) use ($counts) {
135 $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
136 });
137
138 $sorted = $counts->sort();
139
140 $highestValue = $sorted->last();
141
142 return $sorted->filter(function ($value) use ($highestValue) {
143 return $value == $highestValue;
144 })->sort()->keys()->all();
145 }
146
147 /**
148 * Collapse the collection of items into a single array.
149 *
150 * @return static
151 */
152 public function collapse()
153 {
154 return new static(Arr::collapse($this->items));
155 }
156
157 /**
158 * Determine if an item exists in the collection.
159 *
160 * @param mixed $key
161 * @param mixed $operator
162 * @param mixed $value
163 * @return bool
164 */
165 public function contains($key, $operator = null, $value = null)
166 {
167 if (func_num_args() === 1) {
168 if ($this->useAsCallable($key)) {
169 $placeholder = new stdClass;
170
171 return $this->first($key, $placeholder) !== $placeholder;
172 }
173
174 return in_array($key, $this->items);
175 }
176
177 return $this->contains($this->operatorForWhere(...func_get_args()));
178 }
179
180 /**
181 * Determine if an item is not contained in the collection.
182 *
183 * @param mixed $key
184 * @param mixed $operator
185 * @param mixed $value
186 * @return bool
187 */
188 public function doesntContain($key, $operator = null, $value = null)
189 {
190 return ! $this->contains(...func_get_args());
191 }
192
193 /**
194 * Cross join with the given lists, returning all possible permutations.
195 *
196 * @param mixed ...$lists
197 * @return static
198 */
199 public function crossJoin(...$lists)
200 {
201 return new static(Arr::crossJoin(
202 $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
203 ));
204 }
205
206 /**
207 * Get the items in the collection that are not present in the given items.
208 *
209 * @param mixed $items
210 * @return static
211 */
212 public function diff($items)
213 {
214 return new static(array_diff($this->items, $this->getArrayableItems($items)));
215 }
216
217 /**
218 * Get the items in the collection that are not present in the given items, using the callback.
219 *
220 * @param mixed $items
221 * @param callable $callback
222 * @return static
223 */
224 public function diffUsing($items, callable $callback)
225 {
226 return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
227 }
228
229 /**
230 * Get the items in the collection whose keys and values are not present in the given items.
231 *
232 * @param mixed $items
233 * @return static
234 */
235 public function diffAssoc($items)
236 {
237 return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
238 }
239
240 /**
241 * Get the items in the collection whose keys and values are not present in the given items, using the callback.
242 *
243 * @param mixed $items
244 * @param callable $callback
245 * @return static
246 */
247 public function diffAssocUsing($items, callable $callback)
248 {
249 return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
250 }
251
252 /**
253 * Get the items in the collection whose keys are not present in the given items.
254 *
255 * @param mixed $items
256 * @return static
257 */
258 public function diffKeys($items)
259 {
260 return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
261 }
262
263 /**
264 * Get the items in the collection whose keys are not present in the given items, using the callback.
265 *
266 * @param mixed $items
267 * @param callable $callback
268 * @return static
269 */
270 public function diffKeysUsing($items, callable $callback)
271 {
272 return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
273 }
274
275 /**
276 * Retrieve duplicate items from the collection.
277 *
278 * @param callable|string|null $callback
279 * @param bool $strict
280 * @return static
281 */
282 public function duplicates($callback = null, $strict = false)
283 {
284 $items = $this->map($this->valueRetriever($callback));
285
286 $uniqueItems = $items->unique(null, $strict);
287
288 $compare = $this->duplicateComparator($strict);
289
290 $duplicates = new static;
291
292 foreach ($items as $key => $value) {
293 if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
294 $uniqueItems->shift();
295 } else {
296 $duplicates[$key] = $value;
297 }
298 }
299
300 return $duplicates;
301 }
302
303 /**
304 * Retrieve duplicate items from the collection using strict comparison.
305 *
306 * @param callable|string|null $callback
307 * @return static
308 */
309 public function duplicatesStrict($callback = null)
310 {
311 return $this->duplicates($callback, true);
312 }
313
314 /**
315 * Get the comparison function to detect duplicates.
316 *
317 * @param bool $strict
318 * @return \Closure
319 */
320 protected function duplicateComparator($strict)
321 {
322 if ($strict) {
323 return function ($a, $b) {
324 return $a === $b;
325 };
326 }
327
328 return function ($a, $b) {
329 return $a == $b;
330 };
331 }
332
333 /**
334 * Get all items except for those with the specified keys.
335 *
336 * @param \FluentCommunity\Framework\Support\Collection|mixed $keys
337 * @return static
338 */
339 public function except($keys)
340 {
341 if ($keys instanceof Enumerable) {
342 $keys = $keys->all();
343 } elseif (! is_array($keys)) {
344 $keys = func_get_args();
345 }
346
347 return new static(Arr::except($this->items, $keys));
348 }
349
350 /**
351 * Run a filter over each of the items.
352 *
353 * @param callable|null $callback
354 * @return static
355 */
356 public function filter(callable $callback = null)
357 {
358 if ($callback) {
359 return new static(Arr::where($this->items, $callback));
360 }
361
362 return new static(array_filter($this->items));
363 }
364
365 /**
366 * Get the first item from the collection passing the given truth test.
367 *
368 * @param callable|null $callback
369 * @param mixed $default
370 * @return mixed
371 */
372 public function first(callable $callback = null, $default = null)
373 {
374 return Arr::first($this->items, $callback, $default);
375 }
376
377 /**
378 * Get a flattened array of the items in the collection.
379 *
380 * @param int $depth
381 * @return static
382 */
383 public function flatten($depth = INF)
384 {
385 return new static(Arr::flatten($this->items, $depth));
386 }
387
388 /**
389 * Flip the items in the collection.
390 *
391 * @return static
392 */
393 public function flip()
394 {
395 return new static(array_flip($this->items));
396 }
397
398 /**
399 * Remove an item from the collection by key.
400 *
401 * @param string|int|array $keys
402 * @return $this
403 */
404 public function forget($keys)
405 {
406 foreach ((array) $keys as $key) {
407 $this->offsetUnset($key);
408 }
409
410 return $this;
411 }
412
413 /**
414 * Get an item from the collection by key.
415 *
416 * @param mixed $key
417 * @param mixed $default
418 * @return mixed
419 */
420 public function get($key, $default = null)
421 {
422 if (array_key_exists($key, $this->items)) {
423 return $this->items[$key];
424 }
425
426 return Helper::value($default);
427 }
428
429 /**
430 * Get an item from the collection by key or add it to collection if it does not exist.
431 *
432 * @param mixed $key
433 * @param mixed $value
434 * @return mixed
435 */
436 public function getOrPut($key, $value)
437 {
438 if (array_key_exists($key, $this->items)) {
439 return $this->items[$key];
440 }
441
442 $this->offsetSet($key, $value = Helper::value($value)); // @need_fix
443
444 return $value;
445 }
446
447 /**
448 * Group an associative array by a field or using a callback.
449 *
450 * @param array|callable|string $groupBy
451 * @param bool $preserveKeys
452 * @return static
453 */
454 public function groupBy($groupBy, $preserveKeys = false)
455 {
456 if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
457 $nextGroups = $groupBy;
458
459 $groupBy = array_shift($nextGroups);
460 }
461
462 $groupBy = $this->valueRetriever($groupBy);
463
464 $results = [];
465
466 foreach ($this->items as $key => $value) {
467 $groupKeys = $groupBy($value, $key);
468
469 if (! is_array($groupKeys)) {
470 $groupKeys = [$groupKeys];
471 }
472
473 foreach ($groupKeys as $groupKey) {
474 $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
475
476 if (! array_key_exists($groupKey, $results)) {
477 $results[$groupKey] = new static;
478 }
479
480 $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
481 }
482 }
483
484 $result = new static($results);
485
486 if (! empty($nextGroups)) {
487 return $result->map->groupBy($nextGroups, $preserveKeys);
488 }
489
490 return $result;
491 }
492
493 /**
494 * Key an associative array by a field or using a callback.
495 *
496 * @param callable|string $keyBy
497 * @return static
498 */
499 public function keyBy($keyBy)
500 {
501 $keyBy = $this->valueRetriever($keyBy);
502
503 $results = [];
504
505 foreach ($this->items as $key => $item) {
506 $resolvedKey = $keyBy($item, $key);
507
508 if (is_object($resolvedKey)) {
509 $resolvedKey = (string) $resolvedKey;
510 }
511
512 $results[$resolvedKey] = $item;
513 }
514
515 return new static($results);
516 }
517
518 /**
519 * Determine if an item exists in the collection by key.
520 *
521 * @param mixed $key
522 * @return bool
523 */
524 public function has($key)
525 {
526 $keys = is_array($key) ? $key : func_get_args();
527
528 foreach ($keys as $value) {
529 if (! array_key_exists($value, $this->items)) {
530 return false;
531 }
532 }
533
534 return true;
535 }
536
537 /**
538 * Determine if any of the keys exist in the collection.
539 *
540 * @param mixed $key
541 * @return bool
542 */
543 public function hasAny($key)
544 {
545 if ($this->isEmpty()) {
546 return false;
547 }
548
549 $keys = is_array($key) ? $key : func_get_args();
550
551 foreach ($keys as $value) {
552 if ($this->has($value)) {
553 return true;
554 }
555 }
556
557 return false;
558 }
559
560 /**
561 * Concatenate values of a given key as a string.
562 *
563 * @param string $value
564 * @param string|null $glue
565 * @return string
566 */
567 public function implode($value, $glue = null)
568 {
569 $first = $this->first();
570
571 if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
572 return implode($glue ?? '', $this->pluck($value)->all());
573 }
574
575 return implode($value ?? '', $this->items);
576 }
577
578 /**
579 * Intersect the collection with the given items.
580 *
581 * @param mixed $items
582 * @return static
583 */
584 public function intersect($items)
585 {
586 return new static(array_intersect($this->items, $this->getArrayableItems($items)));
587 }
588
589 /**
590 * Intersect the collection with the given items by key.
591 *
592 * @param mixed $items
593 * @return static
594 */
595 public function intersectByKeys($items)
596 {
597 return new static(array_intersect_key(
598 $this->items, $this->getArrayableItems($items)
599 ));
600 }
601
602 /**
603 * Determine if the collection is empty or not.
604 *
605 * @return bool
606 */
607 public function isEmpty()
608 {
609 return empty($this->items);
610 }
611
612 /**
613 * Determine if the collection contains a single item.
614 *
615 * @return bool
616 */
617 public function containsOneItem()
618 {
619 return $this->count() === 1;
620 }
621
622 /**
623 * Join all items from the collection using a string. The final items can use a separate glue string.
624 *
625 * @param string $glue
626 * @param string $finalGlue
627 * @return string
628 */
629 public function join($glue, $finalGlue = '')
630 {
631 if ($finalGlue === '') {
632 return $this->implode($glue);
633 }
634
635 $count = $this->count();
636
637 if ($count === 0) {
638 return '';
639 }
640
641 if ($count === 1) {
642 return $this->last();
643 }
644
645 $collection = new static($this->items);
646
647 $finalItem = $collection->pop();
648
649 return $collection->implode($glue).$finalGlue.$finalItem;
650 }
651
652 /**
653 * Get the keys of the collection items.
654 *
655 * @return static
656 */
657 public function keys()
658 {
659 return new static(array_keys($this->items));
660 }
661
662 /**
663 * Get the last item from the collection.
664 *
665 * @param callable|null $callback
666 * @param mixed $default
667 * @return mixed
668 */
669 public function last(callable $callback = null, $default = null)
670 {
671 return Arr::last($this->items, $callback, $default);
672 }
673
674 /**
675 * Get the values of a given key.
676 *
677 * @param string|array|int|null $value
678 * @param string|null $key
679 * @return static
680 */
681 public function pluck($value, $key = null)
682 {
683 return new static(Arr::pluck($this->items, $value, $key));
684 }
685
686 /**
687 * Run a map over each of the items.
688 *
689 * @param callable $callback
690 * @return static
691 */
692 public function map(callable $callback)
693 {
694 $keys = array_keys($this->items);
695
696 $items = array_map($callback, $this->items, $keys);
697
698 return new static(array_combine($keys, $items));
699 }
700
701 /**
702 * Run a dictionary map over the items.
703 *
704 * The callback should return an associative array with a single key/value pair.
705 *
706 * @param callable $callback
707 * @return static
708 */
709 public function mapToDictionary(callable $callback)
710 {
711 $dictionary = [];
712
713 foreach ($this->items as $key => $item) {
714 $pair = $callback($item, $key);
715
716 $key = key($pair);
717
718 $value = reset($pair);
719
720 if (! isset($dictionary[$key])) {
721 $dictionary[$key] = [];
722 }
723
724 $dictionary[$key][] = $value;
725 }
726
727 return new static($dictionary);
728 }
729
730 /**
731 * Run an associative map over each of the items.
732 *
733 * The callback should return an associative array with a single key/value pair.
734 *
735 * @param callable $callback
736 * @return static
737 */
738 public function mapWithKeys(callable $callback)
739 {
740 $result = [];
741
742 foreach ($this->items as $key => $value) {
743 $assoc = $callback($value, $key);
744
745 foreach ($assoc as $mapKey => $mapValue) {
746 $result[$mapKey] = $mapValue;
747 }
748 }
749
750 return new static($result);
751 }
752
753 /**
754 * Merge the collection with the given items.
755 *
756 * @param mixed $items
757 * @return static
758 */
759 public function merge($items)
760 {
761 return new static(array_merge($this->items, $this->getArrayableItems($items)));
762 }
763
764 /**
765 * Recursively merge the collection with the given items.
766 *
767 * @param mixed $items
768 * @return static
769 */
770 public function mergeRecursive($items)
771 {
772 return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
773 }
774
775 /**
776 * Create a collection by using this collection for keys and another for its values.
777 *
778 * @param mixed $values
779 * @return static
780 */
781 public function combine($values)
782 {
783 return new static(array_combine($this->all(), $this->getArrayableItems($values)));
784 }
785
786 /**
787 * Union the collection with the given items.
788 *
789 * @param mixed $items
790 * @return static
791 */
792 public function union($items)
793 {
794 return new static($this->items + $this->getArrayableItems($items));
795 }
796
797 /**
798 * Create a new collection consisting of every n-th element.
799 *
800 * @param int $step
801 * @param int $offset
802 * @return static
803 */
804 public function nth($step, $offset = 0)
805 {
806 $new = [];
807
808 $position = 0;
809
810 foreach ($this->slice($offset)->items as $item) {
811 if ($position % $step === 0) {
812 $new[] = $item;
813 }
814
815 $position++;
816 }
817
818 return new static($new);
819 }
820
821 /**
822 * Get the items with the specified keys.
823 *
824 * @param mixed $keys
825 * @return static
826 */
827 public function only($keys)
828 {
829 if (is_null($keys)) {
830 return new static($this->items);
831 }
832
833 if ($keys instanceof Enumerable) {
834 $keys = $keys->all();
835 }
836
837 $keys = is_array($keys) ? $keys : func_get_args();
838
839 return new static(Arr::only($this->items, $keys));
840 }
841
842 /**
843 * Get and remove the last N items from the collection.
844 *
845 * @param int $count
846 * @return mixed
847 */
848 public function pop($count = 1)
849 {
850 if ($count === 1) {
851 return array_pop($this->items);
852 }
853
854 if ($this->isEmpty()) {
855 return new static;
856 }
857
858 $results = [];
859
860 $collectionCount = $this->count();
861
862 foreach (range(1, min($count, $collectionCount)) as $item) {
863 array_push($results, array_pop($this->items));
864 }
865
866 return new static($results);
867 }
868
869 /**
870 * Push an item onto the beginning of the collection.
871 *
872 * @param mixed $value
873 * @param mixed $key
874 * @return $this
875 */
876 public function prepend($value, $key = null)
877 {
878 $this->items = Arr::prepend($this->items, ...func_get_args());
879
880 return $this;
881 }
882
883 /**
884 * Push one or more items onto the end of the collection.
885 *
886 * @param mixed $values
887 * @return $this
888 */
889 public function push(...$values)
890 {
891 foreach ($values as $value) {
892 $this->items[] = $value;
893 }
894
895 return $this;
896 }
897
898 /**
899 * Push all of the given items onto the collection.
900 *
901 * @param iterable $source
902 * @return static
903 */
904 public function concat($source)
905 {
906 $result = new static($this);
907
908 foreach ($source as $item) {
909 $result->push($item);
910 }
911
912 return $result;
913 }
914
915 /**
916 * Get and remove an item from the collection.
917 *
918 * @param mixed $key
919 * @param mixed $default
920 * @return mixed
921 */
922 public function pull($key, $default = null)
923 {
924 return Arr::pull($this->items, $key, $default);
925 }
926
927 /**
928 * Put an item in the collection by key.
929 *
930 * @param mixed $key
931 * @param mixed $value
932 * @return $this
933 */
934 public function put($key, $value)
935 {
936 $this->offsetSet($key, $value);
937
938 return $this;
939 }
940
941 /**
942 * Get one or a specified number of items randomly from the collection.
943 *
944 * @param int|null $number
945 * @return static|mixed
946 *
947 * @throws \InvalidArgumentException
948 */
949 public function random($number = null)
950 {
951 if (is_null($number)) {
952 return Arr::random($this->items);
953 }
954
955 return new static(Arr::random($this->items, $number));
956 }
957
958 /**
959 * Replace the collection items with the given items.
960 *
961 * @param mixed $items
962 * @return static
963 */
964 public function replace($items)
965 {
966 return new static(array_replace($this->items, $this->getArrayableItems($items)));
967 }
968
969 /**
970 * Recursively replace the collection items with the given items.
971 *
972 * @param mixed $items
973 * @return static
974 */
975 public function replaceRecursive($items)
976 {
977 return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
978 }
979
980 /**
981 * Reverse items order.
982 *
983 * @return static
984 */
985 public function reverse()
986 {
987 return new static(array_reverse($this->items, true));
988 }
989
990 /**
991 * Search the collection for a given value and return the corresponding key if successful.
992 *
993 * @param mixed $value
994 * @param bool $strict
995 * @return mixed
996 */
997 public function search($value, $strict = false)
998 {
999 if (! $this->useAsCallable($value)) {
1000 return array_search($value, $this->items, $strict);
1001 }
1002
1003 foreach ($this->items as $key => $item) {
1004 if ($value($item, $key)) {
1005 return $key;
1006 }
1007 }
1008
1009 return false;
1010 }
1011
1012 /**
1013 * Get and remove the first N items from the collection.
1014 *
1015 * @param int $count
1016 * @return mixed
1017 */
1018 public function shift($count = 1)
1019 {
1020 if ($count === 1) {
1021 return array_shift($this->items);
1022 }
1023
1024 if ($this->isEmpty()) {
1025 return new static;
1026 }
1027
1028 $results = [];
1029
1030 $collectionCount = $this->count();
1031
1032 foreach (range(1, min($count, $collectionCount)) as $item) {
1033 array_push($results, array_shift($this->items));
1034 }
1035
1036 return new static($results);
1037 }
1038
1039 /**
1040 * Shuffle the items in the collection.
1041 *
1042 * @param int|null $seed
1043 * @return static
1044 */
1045 public function shuffle($seed = null)
1046 {
1047 return new static(Arr::shuffle($this->items, $seed));
1048 }
1049
1050 /**
1051 * Create chunks representing a "sliding window" view of the items in the collection.
1052 *
1053 * @param int $size
1054 * @param int $step
1055 * @return static
1056 */
1057 public function sliding($size = 2, $step = 1)
1058 {
1059 $chunks = floor(($this->count() - $size) / $step) + 1;
1060
1061 return static::times($chunks, function ($number) use ($size, $step) {
1062 return $this->slice(($number - 1) * $step, $size);
1063 });
1064 }
1065
1066 /**
1067 * Skip the first {$count} items.
1068 *
1069 * @param int $count
1070 * @return static
1071 */
1072 public function skip($count)
1073 {
1074 return $this->slice($count);
1075 }
1076
1077 /**
1078 * Skip items in the collection until the given condition is met.
1079 *
1080 * @param mixed $value
1081 * @return static
1082 */
1083 public function skipUntil($value)
1084 {
1085 return new static($this->lazy()->skipUntil($value)->all());
1086 }
1087
1088 /**
1089 * Skip items in the collection while the given condition is met.
1090 *
1091 * @param mixed $value
1092 * @return static
1093 */
1094 public function skipWhile($value)
1095 {
1096 return new static($this->lazy()->skipWhile($value)->all());
1097 }
1098
1099 /**
1100 * Slice the underlying collection array.
1101 *
1102 * @param int $offset
1103 * @param int|null $length
1104 * @return static
1105 */
1106 public function slice($offset, $length = null)
1107 {
1108 return new static(array_slice($this->items, $offset, $length, true));
1109 }
1110
1111 /**
1112 * Split a collection into a certain number of groups.
1113 *
1114 * @param int $numberOfGroups
1115 * @return static
1116 */
1117 public function split($numberOfGroups)
1118 {
1119 if ($this->isEmpty()) {
1120 return new static;
1121 }
1122
1123 $groups = new static;
1124
1125 $groupSize = floor($this->count() / $numberOfGroups);
1126
1127 $remain = $this->count() % $numberOfGroups;
1128
1129 $start = 0;
1130
1131 for ($i = 0; $i < $numberOfGroups; $i++) {
1132 $size = $groupSize;
1133
1134 if ($i < $remain) {
1135 $size++;
1136 }
1137
1138 if ($size) {
1139 $groups->push(new static(array_slice($this->items, $start, $size)));
1140
1141 $start += $size;
1142 }
1143 }
1144
1145 return $groups;
1146 }
1147
1148 /**
1149 * Split a collection into a certain number of groups, and fill the first groups completely.
1150 *
1151 * @param int $numberOfGroups
1152 * @return static
1153 */
1154 public function splitIn($numberOfGroups)
1155 {
1156 return $this->chunk(ceil($this->count() / $numberOfGroups));
1157 }
1158
1159 /**
1160 * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
1161 *
1162 * @param mixed $key
1163 * @param mixed $operator
1164 * @param mixed $value
1165 * @return mixed
1166 *
1167 * @throws \FluentCommunity\Framework\Support\ItemNotFoundException
1168 * @throws \FluentCommunity\Framework\Support\MultipleItemsFoundException
1169 */
1170 public function sole($key = null, $operator = null, $value = null)
1171 {
1172 $filter = func_num_args() > 1
1173 ? $this->operatorForWhere(...func_get_args())
1174 : $key;
1175
1176 $items = $this->when($filter)->filter($filter);
1177
1178 if ($items->isEmpty()) {
1179 throw new ItemNotFoundException;
1180 }
1181
1182 if ($items->count() > 1) {
1183 throw new MultipleItemsFoundException;
1184 }
1185
1186 return $items->first();
1187 }
1188
1189 /**
1190 * Get the first item in the collection but throw an exception if no matching items exist.
1191 *
1192 * @param mixed $key
1193 * @param mixed $operator
1194 * @param mixed $value
1195 * @return mixed
1196 *
1197 * @throws \FluentCommunity\Framework\Support\ItemNotFoundException
1198 */
1199 public function firstOrFail($key = null, $operator = null, $value = null)
1200 {
1201 $filter = func_num_args() > 1
1202 ? $this->operatorForWhere(...func_get_args())
1203 : $key;
1204
1205 $placeholder = new stdClass();
1206
1207 $item = $this->first($filter, $placeholder);
1208
1209 if ($item === $placeholder) {
1210 throw new ItemNotFoundException;
1211 }
1212
1213 return $item;
1214 }
1215
1216 /**
1217 * Chunk the collection into chunks of the given size.
1218 *
1219 * @param int $size
1220 * @return static
1221 */
1222 public function chunk($size)
1223 {
1224 if ($size <= 0) {
1225 return new static;
1226 }
1227
1228 $chunks = [];
1229
1230 foreach (array_chunk($this->items, $size, true) as $chunk) {
1231 $chunks[] = new static($chunk);
1232 }
1233
1234 return new static($chunks);
1235 }
1236
1237 /**
1238 * Chunk the collection into chunks with a callback.
1239 *
1240 * @param callable $callback
1241 * @return static
1242 */
1243 public function chunkWhile(callable $callback)
1244 {
1245 return new static(
1246 $this->lazy()->chunkWhile($callback)->mapInto(static::class)
1247 );
1248 }
1249
1250 /**
1251 * Sort through each item with a callback.
1252 *
1253 * @param callable|int|null $callback
1254 * @return static
1255 */
1256 public function sort($callback = null)
1257 {
1258 $items = $this->items;
1259
1260 $callback && is_callable($callback)
1261 ? uasort($items, $callback)
1262 : asort($items, $callback ?? SORT_REGULAR);
1263
1264 return new static($items);
1265 }
1266
1267 /**
1268 * Sort items in descending order.
1269 *
1270 * @param int $options
1271 * @return static
1272 */
1273 public function sortDesc($options = SORT_REGULAR)
1274 {
1275 $items = $this->items;
1276
1277 arsort($items, $options);
1278
1279 return new static($items);
1280 }
1281
1282 /**
1283 * Sort the collection using the given callback.
1284 *
1285 * @param callable|array|string $callback
1286 * @param int $options
1287 * @param bool $descending
1288 * @return static
1289 */
1290 public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
1291 {
1292 if (is_array($callback) && ! is_callable($callback)) {
1293 return $this->sortByMany($callback);
1294 }
1295
1296 $results = [];
1297
1298 $callback = $this->valueRetriever($callback);
1299
1300 // First we will loop through the items and get the comparator from a callback
1301 // function which we were given. Then, we will sort the returned values and
1302 // grab all the corresponding values for the sorted keys from this array.
1303 foreach ($this->items as $key => $value) {
1304 $results[$key] = $callback($value, $key);
1305 }
1306
1307 $descending ? arsort($results, $options)
1308 : asort($results, $options);
1309
1310 // Once we have sorted all of the keys in the array, we will loop through them
1311 // and grab the corresponding model so we can set the underlying items list
1312 // to the sorted version. Then we'll just return the collection instance.
1313 foreach (array_keys($results) as $key) {
1314 $results[$key] = $this->items[$key];
1315 }
1316
1317 return new static($results);
1318 }
1319
1320 /**
1321 * Sort the collection using multiple comparisons.
1322 *
1323 * @param array $comparisons
1324 * @return static
1325 */
1326 protected function sortByMany(array $comparisons = [])
1327 {
1328 $items = $this->items;
1329
1330 usort($items, function ($a, $b) use ($comparisons) {
1331 foreach ($comparisons as $comparison) {
1332 $comparison = Arr::wrap($comparison);
1333
1334 $prop = $comparison[0];
1335
1336 $ascending = Arr::get($comparison, 1, true) === true ||
1337 Arr::get($comparison, 1, true) === 'asc';
1338
1339 $result = 0;
1340
1341 if (! is_string($prop) && is_callable($prop)) {
1342 $result = $prop($a, $b);
1343 } else {
1344 $values = [Helper::dataGet($a, $prop), Helper::dataGet($b, $prop)];
1345
1346 if (! $ascending) {
1347 $values = array_reverse($values);
1348 }
1349
1350 $result = $values[0] <=> $values[1];
1351 }
1352
1353 if ($result === 0) {
1354 continue;
1355 }
1356
1357 return $result;
1358 }
1359 });
1360
1361 return new static($items);
1362 }
1363
1364 /**
1365 * Sort the collection in descending order using the given callback.
1366 *
1367 * @param callable|string $callback
1368 * @param int $options
1369 * @return static
1370 */
1371 public function sortByDesc($callback, $options = SORT_REGULAR)
1372 {
1373 return $this->sortBy($callback, $options, true);
1374 }
1375
1376 /**
1377 * Sort the collection keys.
1378 *
1379 * @param int $options
1380 * @param bool $descending
1381 * @return static
1382 */
1383 public function sortKeys($options = SORT_REGULAR, $descending = false)
1384 {
1385 $items = $this->items;
1386
1387 $descending ? krsort($items, $options) : ksort($items, $options);
1388
1389 return new static($items);
1390 }
1391
1392 /**
1393 * Sort the collection keys in descending order.
1394 *
1395 * @param int $options
1396 * @return static
1397 */
1398 public function sortKeysDesc($options = SORT_REGULAR)
1399 {
1400 return $this->sortKeys($options, true);
1401 }
1402
1403 /**
1404 * Sort the collection keys using a callback.
1405 *
1406 * @param callable $callback
1407 * @return static
1408 */
1409 public function sortKeysUsing(callable $callback)
1410 {
1411 $items = $this->items;
1412
1413 uksort($items, $callback);
1414
1415 return new static($items);
1416 }
1417
1418 /**
1419 * Splice a portion of the underlying collection array.
1420 *
1421 * @param int $offset
1422 * @param int|null $length
1423 * @param mixed $replacement
1424 * @return static
1425 */
1426 public function splice($offset, $length = null, $replacement = [])
1427 {
1428 if (func_num_args() === 1) {
1429 return new static(array_splice($this->items, $offset));
1430 }
1431
1432 return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement)));
1433 }
1434
1435 /**
1436 * Take the first or last {$limit} items.
1437 *
1438 * @param int $limit
1439 * @return static
1440 */
1441 public function take($limit)
1442 {
1443 if ($limit < 0) {
1444 return $this->slice($limit, abs($limit));
1445 }
1446
1447 return $this->slice(0, $limit);
1448 }
1449
1450 /**
1451 * Take items in the collection until the given condition is met.
1452 *
1453 * @param mixed $value
1454 * @return static
1455 */
1456 public function takeUntil($value)
1457 {
1458 return new static($this->lazy()->takeUntil($value)->all());
1459 }
1460
1461 /**
1462 * Take items in the collection while the given condition is met.
1463 *
1464 * @param mixed $value
1465 * @return static
1466 */
1467 public function takeWhile($value)
1468 {
1469 return new static($this->lazy()->takeWhile($value)->all());
1470 }
1471
1472 /**
1473 * Transform each item in the collection using a callback.
1474 *
1475 * @param callable $callback
1476 * @return $this
1477 */
1478 public function transform(callable $callback)
1479 {
1480 $this->items = $this->map($callback)->all();
1481
1482 return $this;
1483 }
1484
1485 /**
1486 * Convert a flatten "dot" notation array into an expanded array.
1487 *
1488 * @return static
1489 */
1490 public function undot()
1491 {
1492 return new static(Arr::undot($this->all()));
1493 }
1494
1495 /**
1496 * Return only unique items from the collection array.
1497 *
1498 * @param string|callable|null $key
1499 * @param bool $strict
1500 * @return static
1501 */
1502 public function unique($key = null, $strict = false)
1503 {
1504 if (is_null($key) && $strict === false) {
1505 return new static(array_unique($this->items, SORT_REGULAR));
1506 }
1507
1508 $callback = $this->valueRetriever($key);
1509
1510 $exists = [];
1511
1512 return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
1513 if (in_array($id = $callback($item, $key), $exists, $strict)) {
1514 return true;
1515 }
1516
1517 $exists[] = $id;
1518 });
1519 }
1520
1521 /**
1522 * Reset the keys on the underlying array.
1523 *
1524 * @return static
1525 */
1526 public function values()
1527 {
1528 return new static(array_values($this->items));
1529 }
1530
1531 /**
1532 * Zip the collection together with one or more arrays.
1533 *
1534 * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1535 * => [[1, 4], [2, 5], [3, 6]]
1536 *
1537 * @param mixed ...$items
1538 * @return static
1539 */
1540 public function zip($items)
1541 {
1542 $arrayableItems = array_map(function ($items) {
1543 return $this->getArrayableItems($items);
1544 }, func_get_args());
1545
1546 $params = array_merge([function () {
1547 return new static(func_get_args());
1548 }, $this->items], $arrayableItems);
1549
1550 return new static(array_map(...$params));
1551 }
1552
1553 /**
1554 * Pad collection to the specified length with a value.
1555 *
1556 * @param int $size
1557 * @param mixed $value
1558 * @return static
1559 */
1560 public function pad($size, $value)
1561 {
1562 return new static(array_pad($this->items, $size, $value));
1563 }
1564
1565 /**
1566 * Get an iterator for the items.
1567 *
1568 * @return \ArrayIterator
1569 */
1570 #[\ReturnTypeWillChange]
1571 public function getIterator()
1572 {
1573 return new ArrayIterator($this->items);
1574 }
1575
1576 /**
1577 * Count the number of items in the collection.
1578 *
1579 * @return int
1580 */
1581 #[\ReturnTypeWillChange]
1582 public function count()
1583 {
1584 return count($this->items);
1585 }
1586
1587 /**
1588 * Count the number of items in the collection by a field or using a callback.
1589 *
1590 * @param callable|string $countBy
1591 * @return static
1592 */
1593 public function countBy($countBy = null)
1594 {
1595 return new static($this->lazy()->countBy($countBy)->all());
1596 }
1597
1598 /**
1599 * Add an item to the collection.
1600 *
1601 * @param mixed $item
1602 * @return $this
1603 */
1604 public function add($item)
1605 {
1606 $this->items[] = $item;
1607
1608 return $this;
1609 }
1610
1611 /**
1612 * Get a base Support collection instance from this collection.
1613 *
1614 * @return \FluentCommunity\Framework\Support\Collection
1615 */
1616 public function toBase()
1617 {
1618 return new self($this);
1619 }
1620
1621 /**
1622 * Determine if an item exists at an offset.
1623 *
1624 * @param mixed $key
1625 * @return bool
1626 */
1627 #[\ReturnTypeWillChange]
1628 public function offsetExists($key)
1629 {
1630 return isset($this->items[$key]);
1631 }
1632
1633 /**
1634 * Get an item at a given offset.
1635 *
1636 * @param mixed $key
1637 * @return mixed
1638 */
1639 #[\ReturnTypeWillChange]
1640 public function offsetGet($key)
1641 {
1642 return $this->items[$key];
1643 }
1644
1645 /**
1646 * Set the item at a given offset.
1647 *
1648 * @param mixed $key
1649 * @param mixed $value
1650 * @return void
1651 */
1652 #[\ReturnTypeWillChange]
1653 public function offsetSet($key, $value)
1654 {
1655 if (is_null($key)) {
1656 $this->items[] = $value;
1657 } else {
1658 $this->items[$key] = $value;
1659 }
1660 }
1661
1662 /**
1663 * Unset the item at a given offset.
1664 *
1665 * @param mixed $key
1666 * @return void
1667 */
1668 #[\ReturnTypeWillChange]
1669 public function offsetUnset($key)
1670 {
1671 unset($this->items[$key]);
1672 }
1673 }
1674