1<?php 2 3namespace Illuminate\Support; 4 5use ArrayAccess; 6use ArrayIterator; 7use Illuminate\Collections\ItemNotFoundException; 8use Illuminate\Collections\MultipleItemsFoundException; 9use Illuminate\Support\Traits\EnumeratesValues; 10use Illuminate\Support\Traits\Macroable; 11use stdClass; 12 13class Collection implements ArrayAccess, Enumerable 14{ 15 use EnumeratesValues, Macroable; 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 \Illuminate\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 * Cross join with the given lists, returning all possible permutations. 182 * 183 * @param mixed ...$lists 184 * @return static 185 */ 186 public function crossJoin(...$lists) 187 { 188 return new static(Arr::crossJoin( 189 $this->items, ...array_map([$this, 'getArrayableItems'], $lists) 190 )); 191 } 192 193 /** 194 * Get the items in the collection that are not present in the given items. 195 * 196 * @param mixed $items 197 * @return static 198 */ 199 public function diff($items) 200 { 201 return new static(array_diff($this->items, $this->getArrayableItems($items))); 202 } 203 204 /** 205 * Get the items in the collection that are not present in the given items, using the callback. 206 * 207 * @param mixed $items 208 * @param callable $callback 209 * @return static 210 */ 211 public function diffUsing($items, callable $callback) 212 { 213 return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback)); 214 } 215 216 /** 217 * Get the items in the collection whose keys and values are not present in the given items. 218 * 219 * @param mixed $items 220 * @return static 221 */ 222 public function diffAssoc($items) 223 { 224 return new static(array_diff_assoc($this->items, $this->getArrayableItems($items))); 225 } 226 227 /** 228 * Get the items in the collection whose keys and values are not present in the given items, using the callback. 229 * 230 * @param mixed $items 231 * @param callable $callback 232 * @return static 233 */ 234 public function diffAssocUsing($items, callable $callback) 235 { 236 return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback)); 237 } 238 239 /** 240 * Get the items in the collection whose keys are not present in the given items. 241 * 242 * @param mixed $items 243 * @return static 244 */ 245 public function diffKeys($items) 246 { 247 return new static(array_diff_key($this->items, $this->getArrayableItems($items))); 248 } 249 250 /** 251 * Get the items in the collection whose keys are not present in the given items, using the callback. 252 * 253 * @param mixed $items 254 * @param callable $callback 255 * @return static 256 */ 257 public function diffKeysUsing($items, callable $callback) 258 { 259 return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); 260 } 261 262 /** 263 * Retrieve duplicate items from the collection. 264 * 265 * @param callable|string|null $callback 266 * @param bool $strict 267 * @return static 268 */ 269 public function duplicates($callback = null, $strict = false) 270 { 271 $items = $this->map($this->valueRetriever($callback)); 272 273 $uniqueItems = $items->unique(null, $strict); 274 275 $compare = $this->duplicateComparator($strict); 276 277 $duplicates = new static; 278 279 foreach ($items as $key => $value) { 280 if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) { 281 $uniqueItems->shift(); 282 } else { 283 $duplicates[$key] = $value; 284 } 285 } 286 287 return $duplicates; 288 } 289 290 /** 291 * Retrieve duplicate items from the collection using strict comparison. 292 * 293 * @param callable|string|null $callback 294 * @return static 295 */ 296 public function duplicatesStrict($callback = null) 297 { 298 return $this->duplicates($callback, true); 299 } 300 301 /** 302 * Get the comparison function to detect duplicates. 303 * 304 * @param bool $strict 305 * @return \Closure 306 */ 307 protected function duplicateComparator($strict) 308 { 309 if ($strict) { 310 return function ($a, $b) { 311 return $a === $b; 312 }; 313 } 314 315 return function ($a, $b) { 316 return $a == $b; 317 }; 318 } 319 320 /** 321 * Get all items except for those with the specified keys. 322 * 323 * @param \Illuminate\Support\Collection|mixed $keys 324 * @return static 325 */ 326 public function except($keys) 327 { 328 if ($keys instanceof Enumerable) { 329 $keys = $keys->all(); 330 } elseif (! is_array($keys)) { 331 $keys = func_get_args(); 332 } 333 334 return new static(Arr::except($this->items, $keys)); 335 } 336 337 /** 338 * Run a filter over each of the items. 339 * 340 * @param callable|null $callback 341 * @return static 342 */ 343 public function filter(callable $callback = null) 344 { 345 if ($callback) { 346 return new static(Arr::where($this->items, $callback)); 347 } 348 349 return new static(array_filter($this->items)); 350 } 351 352 /** 353 * Get the first item from the collection passing the given truth test. 354 * 355 * @param callable|null $callback 356 * @param mixed $default 357 * @return mixed 358 */ 359 public function first(callable $callback = null, $default = null) 360 { 361 return Arr::first($this->items, $callback, $default); 362 } 363 364 /** 365 * Get a flattened array of the items in the collection. 366 * 367 * @param int $depth 368 * @return static 369 */ 370 public function flatten($depth = INF) 371 { 372 return new static(Arr::flatten($this->items, $depth)); 373 } 374 375 /** 376 * Flip the items in the collection. 377 * 378 * @return static 379 */ 380 public function flip() 381 { 382 return new static(array_flip($this->items)); 383 } 384 385 /** 386 * Remove an item from the collection by key. 387 * 388 * @param string|array $keys 389 * @return $this 390 */ 391 public function forget($keys) 392 { 393 foreach ((array) $keys as $key) { 394 $this->offsetUnset($key); 395 } 396 397 return $this; 398 } 399 400 /** 401 * Get an item from the collection by key. 402 * 403 * @param mixed $key 404 * @param mixed $default 405 * @return mixed 406 */ 407 public function get($key, $default = null) 408 { 409 if (array_key_exists($key, $this->items)) { 410 return $this->items[$key]; 411 } 412 413 return value($default); 414 } 415 416 /** 417 * Group an associative array by a field or using a callback. 418 * 419 * @param array|callable|string $groupBy 420 * @param bool $preserveKeys 421 * @return static 422 */ 423 public function groupBy($groupBy, $preserveKeys = false) 424 { 425 if (! $this->useAsCallable($groupBy) && is_array($groupBy)) { 426 $nextGroups = $groupBy; 427 428 $groupBy = array_shift($nextGroups); 429 } 430 431 $groupBy = $this->valueRetriever($groupBy); 432 433 $results = []; 434 435 foreach ($this->items as $key => $value) { 436 $groupKeys = $groupBy($value, $key); 437 438 if (! is_array($groupKeys)) { 439 $groupKeys = [$groupKeys]; 440 } 441 442 foreach ($groupKeys as $groupKey) { 443 $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey; 444 445 if (! array_key_exists($groupKey, $results)) { 446 $results[$groupKey] = new static; 447 } 448 449 $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); 450 } 451 } 452 453 $result = new static($results); 454 455 if (! empty($nextGroups)) { 456 return $result->map->groupBy($nextGroups, $preserveKeys); 457 } 458 459 return $result; 460 } 461 462 /** 463 * Key an associative array by a field or using a callback. 464 * 465 * @param callable|string $keyBy 466 * @return static 467 */ 468 public function keyBy($keyBy) 469 { 470 $keyBy = $this->valueRetriever($keyBy); 471 472 $results = []; 473 474 foreach ($this->items as $key => $item) { 475 $resolvedKey = $keyBy($item, $key); 476 477 if (is_object($resolvedKey)) { 478 $resolvedKey = (string) $resolvedKey; 479 } 480 481 $results[$resolvedKey] = $item; 482 } 483 484 return new static($results); 485 } 486 487 /** 488 * Determine if an item exists in the collection by key. 489 * 490 * @param mixed $key 491 * @return bool 492 */ 493 public function has($key) 494 { 495 $keys = is_array($key) ? $key : func_get_args(); 496 497 foreach ($keys as $value) { 498 if (! array_key_exists($value, $this->items)) { 499 return false; 500 } 501 } 502 503 return true; 504 } 505 506 /** 507 * Concatenate values of a given key as a string. 508 * 509 * @param string $value 510 * @param string|null $glue 511 * @return string 512 */ 513 public function implode($value, $glue = null) 514 { 515 $first = $this->first(); 516 517 if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) { 518 return implode($glue ?? '', $this->pluck($value)->all()); 519 } 520 521 return implode($value ?? '', $this->items); 522 } 523 524 /** 525 * Intersect the collection with the given items. 526 * 527 * @param mixed $items 528 * @return static 529 */ 530 public function intersect($items) 531 { 532 return new static(array_intersect($this->items, $this->getArrayableItems($items))); 533 } 534 535 /** 536 * Intersect the collection with the given items by key. 537 * 538 * @param mixed $items 539 * @return static 540 */ 541 public function intersectByKeys($items) 542 { 543 return new static(array_intersect_key( 544 $this->items, $this->getArrayableItems($items) 545 )); 546 } 547 548 /** 549 * Determine if the collection is empty or not. 550 * 551 * @return bool 552 */ 553 public function isEmpty() 554 { 555 return empty($this->items); 556 } 557 558 /** 559 * Determine if the collection contains a single item. 560 * 561 * @return bool 562 */ 563 public function containsOneItem() 564 { 565 return $this->count() === 1; 566 } 567 568 /** 569 * Join all items from the collection using a string. The final items can use a separate glue string. 570 * 571 * @param string $glue 572 * @param string $finalGlue 573 * @return string 574 */ 575 public function join($glue, $finalGlue = '') 576 { 577 if ($finalGlue === '') { 578 return $this->implode($glue); 579 } 580 581 $count = $this->count(); 582 583 if ($count === 0) { 584 return ''; 585 } 586 587 if ($count === 1) { 588 return $this->last(); 589 } 590 591 $collection = new static($this->items); 592 593 $finalItem = $collection->pop(); 594 595 return $collection->implode($glue).$finalGlue.$finalItem; 596 } 597 598 /** 599 * Get the keys of the collection items. 600 * 601 * @return static 602 */ 603 public function keys() 604 { 605 return new static(array_keys($this->items)); 606 } 607 608 /** 609 * Get the last item from the collection. 610 * 611 * @param callable|null $callback 612 * @param mixed $default 613 * @return mixed 614 */ 615 public function last(callable $callback = null, $default = null) 616 { 617 return Arr::last($this->items, $callback, $default); 618 } 619 620 /** 621 * Get the values of a given key. 622 * 623 * @param string|array|int|null $value 624 * @param string|null $key 625 * @return static 626 */ 627 public function pluck($value, $key = null) 628 { 629 return new static(Arr::pluck($this->items, $value, $key)); 630 } 631 632 /** 633 * Run a map over each of the items. 634 * 635 * @param callable $callback 636 * @return static 637 */ 638 public function map(callable $callback) 639 { 640 $keys = array_keys($this->items); 641 642 $items = array_map($callback, $this->items, $keys); 643 644 return new static(array_combine($keys, $items)); 645 } 646 647 /** 648 * Run a dictionary map over the items. 649 * 650 * The callback should return an associative array with a single key/value pair. 651 * 652 * @param callable $callback 653 * @return static 654 */ 655 public function mapToDictionary(callable $callback) 656 { 657 $dictionary = []; 658 659 foreach ($this->items as $key => $item) { 660 $pair = $callback($item, $key); 661 662 $key = key($pair); 663 664 $value = reset($pair); 665 666 if (! isset($dictionary[$key])) { 667 $dictionary[$key] = []; 668 } 669 670 $dictionary[$key][] = $value; 671 } 672 673 return new static($dictionary); 674 } 675 676 /** 677 * Run an associative map over each of the items. 678 * 679 * The callback should return an associative array with a single key/value pair. 680 * 681 * @param callable $callback 682 * @return static 683 */ 684 public function mapWithKeys(callable $callback) 685 { 686 $result = []; 687 688 foreach ($this->items as $key => $value) { 689 $assoc = $callback($value, $key); 690 691 foreach ($assoc as $mapKey => $mapValue) { 692 $result[$mapKey] = $mapValue; 693 } 694 } 695 696 return new static($result); 697 } 698 699 /** 700 * Merge the collection with the given items. 701 * 702 * @param mixed $items 703 * @return static 704 */ 705 public function merge($items) 706 { 707 return new static(array_merge($this->items, $this->getArrayableItems($items))); 708 } 709 710 /** 711 * Recursively merge the collection with the given items. 712 * 713 * @param mixed $items 714 * @return static 715 */ 716 public function mergeRecursive($items) 717 { 718 return new static(array_merge_recursive($this->items, $this->getArrayableItems($items))); 719 } 720 721 /** 722 * Create a collection by using this collection for keys and another for its values. 723 * 724 * @param mixed $values 725 * @return static 726 */ 727 public function combine($values) 728 { 729 return new static(array_combine($this->all(), $this->getArrayableItems($values))); 730 } 731 732 /** 733 * Union the collection with the given items. 734 * 735 * @param mixed $items 736 * @return static 737 */ 738 public function union($items) 739 { 740 return new static($this->items + $this->getArrayableItems($items)); 741 } 742 743 /** 744 * Create a new collection consisting of every n-th element. 745 * 746 * @param int $step 747 * @param int $offset 748 * @return static 749 */ 750 public function nth($step, $offset = 0) 751 { 752 $new = []; 753 754 $position = 0; 755 756 foreach ($this->items as $item) { 757 if ($position % $step === $offset) { 758 $new[] = $item; 759 } 760 761 $position++; 762 } 763 764 return new static($new); 765 } 766 767 /** 768 * Get the items with the specified keys. 769 * 770 * @param mixed $keys 771 * @return static 772 */ 773 public function only($keys) 774 { 775 if (is_null($keys)) { 776 return new static($this->items); 777 } 778 779 if ($keys instanceof Enumerable) { 780 $keys = $keys->all(); 781 } 782 783 $keys = is_array($keys) ? $keys : func_get_args(); 784 785 return new static(Arr::only($this->items, $keys)); 786 } 787 788 /** 789 * Get and remove the last item from the collection. 790 * 791 * @return mixed 792 */ 793 public function pop() 794 { 795 return array_pop($this->items); 796 } 797 798 /** 799 * Push an item onto the beginning of the collection. 800 * 801 * @param mixed $value 802 * @param mixed $key 803 * @return $this 804 */ 805 public function prepend($value, $key = null) 806 { 807 $this->items = Arr::prepend($this->items, ...func_get_args()); 808 809 return $this; 810 } 811 812 /** 813 * Push one or more items onto the end of the collection. 814 * 815 * @param mixed $values [optional] 816 * @return $this 817 */ 818 public function push(...$values) 819 { 820 foreach ($values as $value) { 821 $this->items[] = $value; 822 } 823 824 return $this; 825 } 826 827 /** 828 * Push all of the given items onto the collection. 829 * 830 * @param iterable $source 831 * @return static 832 */ 833 public function concat($source) 834 { 835 $result = new static($this); 836 837 foreach ($source as $item) { 838 $result->push($item); 839 } 840 841 return $result; 842 } 843 844 /** 845 * Get and remove an item from the collection. 846 * 847 * @param mixed $key 848 * @param mixed $default 849 * @return mixed 850 */ 851 public function pull($key, $default = null) 852 { 853 return Arr::pull($this->items, $key, $default); 854 } 855 856 /** 857 * Put an item in the collection by key. 858 * 859 * @param mixed $key 860 * @param mixed $value 861 * @return $this 862 */ 863 public function put($key, $value) 864 { 865 $this->offsetSet($key, $value); 866 867 return $this; 868 } 869 870 /** 871 * Get one or a specified number of items randomly from the collection. 872 * 873 * @param int|null $number 874 * @return static|mixed 875 * 876 * @throws \InvalidArgumentException 877 */ 878 public function random($number = null) 879 { 880 if (is_null($number)) { 881 return Arr::random($this->items); 882 } 883 884 return new static(Arr::random($this->items, $number)); 885 } 886 887 /** 888 * Replace the collection items with the given items. 889 * 890 * @param mixed $items 891 * @return static 892 */ 893 public function replace($items) 894 { 895 return new static(array_replace($this->items, $this->getArrayableItems($items))); 896 } 897 898 /** 899 * Recursively replace the collection items with the given items. 900 * 901 * @param mixed $items 902 * @return static 903 */ 904 public function replaceRecursive($items) 905 { 906 return new static(array_replace_recursive($this->items, $this->getArrayableItems($items))); 907 } 908 909 /** 910 * Reverse items order. 911 * 912 * @return static 913 */ 914 public function reverse() 915 { 916 return new static(array_reverse($this->items, true)); 917 } 918 919 /** 920 * Search the collection for a given value and return the corresponding key if successful. 921 * 922 * @param mixed $value 923 * @param bool $strict 924 * @return mixed 925 */ 926 public function search($value, $strict = false) 927 { 928 if (! $this->useAsCallable($value)) { 929 return array_search($value, $this->items, $strict); 930 } 931 932 foreach ($this->items as $key => $item) { 933 if ($value($item, $key)) { 934 return $key; 935 } 936 } 937 938 return false; 939 } 940 941 /** 942 * Get and remove the first item from the collection. 943 * 944 * @return mixed 945 */ 946 public function shift() 947 { 948 return array_shift($this->items); 949 } 950 951 /** 952 * Shuffle the items in the collection. 953 * 954 * @param int|null $seed 955 * @return static 956 */ 957 public function shuffle($seed = null) 958 { 959 return new static(Arr::shuffle($this->items, $seed)); 960 } 961 962 /** 963 * Skip the first {$count} items. 964 * 965 * @param int $count 966 * @return static 967 */ 968 public function skip($count) 969 { 970 return $this->slice($count); 971 } 972 973 /** 974 * Skip items in the collection until the given condition is met. 975 * 976 * @param mixed $value 977 * @return static 978 */ 979 public function skipUntil($value) 980 { 981 return new static($this->lazy()->skipUntil($value)->all()); 982 } 983 984 /** 985 * Skip items in the collection while the given condition is met. 986 * 987 * @param mixed $value 988 * @return static 989 */ 990 public function skipWhile($value) 991 { 992 return new static($this->lazy()->skipWhile($value)->all()); 993 } 994 995 /** 996 * Slice the underlying collection array. 997 * 998 * @param int $offset 999 * @param int|null $length 1000 * @return static 1001 */ 1002 public function slice($offset, $length = null) 1003 { 1004 return new static(array_slice($this->items, $offset, $length, true)); 1005 } 1006 1007 /** 1008 * Split a collection into a certain number of groups. 1009 * 1010 * @param int $numberOfGroups 1011 * @return static 1012 */ 1013 public function split($numberOfGroups) 1014 { 1015 if ($this->isEmpty()) { 1016 return new static; 1017 } 1018 1019 $groups = new static; 1020 1021 $groupSize = floor($this->count() / $numberOfGroups); 1022 1023 $remain = $this->count() % $numberOfGroups; 1024 1025 $start = 0; 1026 1027 for ($i = 0; $i < $numberOfGroups; $i++) { 1028 $size = $groupSize; 1029 1030 if ($i < $remain) { 1031 $size++; 1032 } 1033 1034 if ($size) { 1035 $groups->push(new static(array_slice($this->items, $start, $size))); 1036 1037 $start += $size; 1038 } 1039 } 1040 1041 return $groups; 1042 } 1043 1044 /** 1045 * Split a collection into a certain number of groups, and fill the first groups completely. 1046 * 1047 * @param int $numberOfGroups 1048 * @return static 1049 */ 1050 public function splitIn($numberOfGroups) 1051 { 1052 return $this->chunk(ceil($this->count() / $numberOfGroups)); 1053 } 1054 1055 /** 1056 * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. 1057 * 1058 * @param mixed $key 1059 * @param mixed $operator 1060 * @param mixed $value 1061 * @return mixed 1062 * 1063 * @throws \Illuminate\Collections\ItemNotFoundException 1064 * @throws \Illuminate\Collections\MultipleItemsFoundException 1065 */ 1066 public function sole($key = null, $operator = null, $value = null) 1067 { 1068 $filter = func_num_args() > 1 1069 ? $this->operatorForWhere(...func_get_args()) 1070 : $key; 1071 1072 $items = $this->when($filter)->filter($filter); 1073 1074 if ($items->isEmpty()) { 1075 throw new ItemNotFoundException; 1076 } 1077 1078 if ($items->count() > 1) { 1079 throw new MultipleItemsFoundException; 1080 } 1081 1082 return $items->first(); 1083 } 1084 1085 /** 1086 * Chunk the collection into chunks of the given size. 1087 * 1088 * @param int $size 1089 * @return static 1090 */ 1091 public function chunk($size) 1092 { 1093 if ($size <= 0) { 1094 return new static; 1095 } 1096 1097 $chunks = []; 1098 1099 foreach (array_chunk($this->items, $size, true) as $chunk) { 1100 $chunks[] = new static($chunk); 1101 } 1102 1103 return new static($chunks); 1104 } 1105 1106 /** 1107 * Chunk the collection into chunks with a callback. 1108 * 1109 * @param callable $callback 1110 * @return static 1111 */ 1112 public function chunkWhile(callable $callback) 1113 { 1114 return new static( 1115 $this->lazy()->chunkWhile($callback)->mapInto(static::class) 1116 ); 1117 } 1118 1119 /** 1120 * Sort through each item with a callback. 1121 * 1122 * @param callable|int|null $callback 1123 * @return static 1124 */ 1125 public function sort($callback = null) 1126 { 1127 $items = $this->items; 1128 1129 $callback && is_callable($callback) 1130 ? uasort($items, $callback) 1131 : asort($items, $callback ?? SORT_REGULAR); 1132 1133 return new static($items); 1134 } 1135 1136 /** 1137 * Sort items in descending order. 1138 * 1139 * @param int $options 1140 * @return static 1141 */ 1142 public function sortDesc($options = SORT_REGULAR) 1143 { 1144 $items = $this->items; 1145 1146 arsort($items, $options); 1147 1148 return new static($items); 1149 } 1150 1151 /** 1152 * Sort the collection using the given callback. 1153 * 1154 * @param callable|array|string $callback 1155 * @param int $options 1156 * @param bool $descending 1157 * @return static 1158 */ 1159 public function sortBy($callback, $options = SORT_REGULAR, $descending = false) 1160 { 1161 if (is_array($callback) && ! is_callable($callback)) { 1162 return $this->sortByMany($callback); 1163 } 1164 1165 $results = []; 1166 1167 $callback = $this->valueRetriever($callback); 1168 1169 // First we will loop through the items and get the comparator from a callback 1170 // function which we were given. Then, we will sort the returned values and 1171 // and grab the corresponding values for the sorted keys from this array. 1172 foreach ($this->items as $key => $value) { 1173 $results[$key] = $callback($value, $key); 1174 } 1175 1176 $descending ? arsort($results, $options) 1177 : asort($results, $options); 1178 1179 // Once we have sorted all of the keys in the array, we will loop through them 1180 // and grab the corresponding model so we can set the underlying items list 1181 // to the sorted version. Then we'll just return the collection instance. 1182 foreach (array_keys($results) as $key) { 1183 $results[$key] = $this->items[$key]; 1184 } 1185 1186 return new static($results); 1187 } 1188 1189 /** 1190 * Sort the collection using multiple comparisons. 1191 * 1192 * @param array $comparisons 1193 * @return static 1194 */ 1195 protected function sortByMany(array $comparisons = []) 1196 { 1197 $items = $this->items; 1198 1199 usort($items, function ($a, $b) use ($comparisons) { 1200 foreach ($comparisons as $comparison) { 1201 $comparison = Arr::wrap($comparison); 1202 1203 $prop = $comparison[0]; 1204 1205 $ascending = Arr::get($comparison, 1, true) === true || 1206 Arr::get($comparison, 1, true) === 'asc'; 1207 1208 $result = 0; 1209 1210 if (is_callable($prop)) { 1211 $result = $prop($a, $b); 1212 } else { 1213 $values = [data_get($a, $prop), data_get($b, $prop)]; 1214 1215 if (! $ascending) { 1216 $values = array_reverse($values); 1217 } 1218 1219 $result = $values[0] <=> $values[1]; 1220 } 1221 1222 if ($result === 0) { 1223 continue; 1224 } 1225 1226 return $result; 1227 } 1228 }); 1229 1230 return new static($items); 1231 } 1232 1233 /** 1234 * Sort the collection in descending order using the given callback. 1235 * 1236 * @param callable|string $callback 1237 * @param int $options 1238 * @return static 1239 */ 1240 public function sortByDesc($callback, $options = SORT_REGULAR) 1241 { 1242 return $this->sortBy($callback, $options, true); 1243 } 1244 1245 /** 1246 * Sort the collection keys. 1247 * 1248 * @param int $options 1249 * @param bool $descending 1250 * @return static 1251 */ 1252 public function sortKeys($options = SORT_REGULAR, $descending = false) 1253 { 1254 $items = $this->items; 1255 1256 $descending ? krsort($items, $options) : ksort($items, $options); 1257 1258 return new static($items); 1259 } 1260 1261 /** 1262 * Sort the collection keys in descending order. 1263 * 1264 * @param int $options 1265 * @return static 1266 */ 1267 public function sortKeysDesc($options = SORT_REGULAR) 1268 { 1269 return $this->sortKeys($options, true); 1270 } 1271 1272 /** 1273 * Splice a portion of the underlying collection array. 1274 * 1275 * @param int $offset 1276 * @param int|null $length 1277 * @param mixed $replacement 1278 * @return static 1279 */ 1280 public function splice($offset, $length = null, $replacement = []) 1281 { 1282 if (func_num_args() === 1) { 1283 return new static(array_splice($this->items, $offset)); 1284 } 1285 1286 return new static(array_splice($this->items, $offset, $length, $replacement)); 1287 } 1288 1289 /** 1290 * Take the first or last {$limit} items. 1291 * 1292 * @param int $limit 1293 * @return static 1294 */ 1295 public function take($limit) 1296 { 1297 if ($limit < 0) { 1298 return $this->slice($limit, abs($limit)); 1299 } 1300 1301 return $this->slice(0, $limit); 1302 } 1303 1304 /** 1305 * Take items in the collection until the given condition is met. 1306 * 1307 * @param mixed $value 1308 * @return static 1309 */ 1310 public function takeUntil($value) 1311 { 1312 return new static($this->lazy()->takeUntil($value)->all()); 1313 } 1314 1315 /** 1316 * Take items in the collection while the given condition is met. 1317 * 1318 * @param mixed $value 1319 * @return static 1320 */ 1321 public function takeWhile($value) 1322 { 1323 return new static($this->lazy()->takeWhile($value)->all()); 1324 } 1325 1326 /** 1327 * Transform each item in the collection using a callback. 1328 * 1329 * @param callable $callback 1330 * @return $this 1331 */ 1332 public function transform(callable $callback) 1333 { 1334 $this->items = $this->map($callback)->all(); 1335 1336 return $this; 1337 } 1338 1339 /** 1340 * Reset the keys on the underlying array. 1341 * 1342 * @return static 1343 */ 1344 public function values() 1345 { 1346 return new static(array_values($this->items)); 1347 } 1348 1349 /** 1350 * Zip the collection together with one or more arrays. 1351 * 1352 * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); 1353 * => [[1, 4], [2, 5], [3, 6]] 1354 * 1355 * @param mixed ...$items 1356 * @return static 1357 */ 1358 public function zip($items) 1359 { 1360 $arrayableItems = array_map(function ($items) { 1361 return $this->getArrayableItems($items); 1362 }, func_get_args()); 1363 1364 $params = array_merge([function () { 1365 return new static(func_get_args()); 1366 }, $this->items], $arrayableItems); 1367 1368 return new static(array_map(...$params)); 1369 } 1370 1371 /** 1372 * Pad collection to the specified length with a value. 1373 * 1374 * @param int $size 1375 * @param mixed $value 1376 * @return static 1377 */ 1378 public function pad($size, $value) 1379 { 1380 return new static(array_pad($this->items, $size, $value)); 1381 } 1382 1383 /** 1384 * Get an iterator for the items. 1385 * 1386 * @return \ArrayIterator 1387 */ 1388 public function getIterator() 1389 { 1390 return new ArrayIterator($this->items); 1391 } 1392 1393 /** 1394 * Count the number of items in the collection. 1395 * 1396 * @return int 1397 */ 1398 public function count() 1399 { 1400 return count($this->items); 1401 } 1402 1403 /** 1404 * Count the number of items in the collection by a field or using a callback. 1405 * 1406 * @param callable|string $countBy 1407 * @return static 1408 */ 1409 public function countBy($countBy = null) 1410 { 1411 return new static($this->lazy()->countBy($countBy)->all()); 1412 } 1413 1414 /** 1415 * Add an item to the collection. 1416 * 1417 * @param mixed $item 1418 * @return $this 1419 */ 1420 public function add($item) 1421 { 1422 $this->items[] = $item; 1423 1424 return $this; 1425 } 1426 1427 /** 1428 * Get a base Support collection instance from this collection. 1429 * 1430 * @return \Illuminate\Support\Collection 1431 */ 1432 public function toBase() 1433 { 1434 return new self($this); 1435 } 1436 1437 /** 1438 * Determine if an item exists at an offset. 1439 * 1440 * @param mixed $key 1441 * @return bool 1442 */ 1443 public function offsetExists($key) 1444 { 1445 return isset($this->items[$key]); 1446 } 1447 1448 /** 1449 * Get an item at a given offset. 1450 * 1451 * @param mixed $key 1452 * @return mixed 1453 */ 1454 public function offsetGet($key) 1455 { 1456 return $this->items[$key]; 1457 } 1458 1459 /** 1460 * Set the item at a given offset. 1461 * 1462 * @param mixed $key 1463 * @param mixed $value 1464 * @return void 1465 */ 1466 public function offsetSet($key, $value) 1467 { 1468 if (is_null($key)) { 1469 $this->items[] = $value; 1470 } else { 1471 $this->items[$key] = $value; 1472 } 1473 } 1474 1475 /** 1476 * Unset the item at a given offset. 1477 * 1478 * @param string $key 1479 * @return void 1480 */ 1481 public function offsetUnset($key) 1482 { 1483 unset($this->items[$key]); 1484 } 1485} 1486