LazyCollection.php 51 KB

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