PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.91.6
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.91.6
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Support / Collection.php

Collection.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.91.6, at vendor/wpfluent/framework/src/WPFluent/Support/Collection.php

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