LazyCollection.php 51 KB

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