Просмотр файла vendor/laravel/framework/src/Illuminate/Collections/LazyCollection.php

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