Collection.php 53 KB

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