Collection.php 49 KB

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