EnumeratesValues.php 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175
  1. <?php
  2. namespace Illuminate\Support\Traits;
  3. use BackedEnum;
  4. use CachingIterator;
  5. use Closure;
  6. use Exception;
  7. use Illuminate\Contracts\Support\Arrayable;
  8. use Illuminate\Contracts\Support\Jsonable;
  9. use Illuminate\Support\Arr;
  10. use Illuminate\Support\Collection;
  11. use Illuminate\Support\Enumerable;
  12. use Illuminate\Support\HigherOrderCollectionProxy;
  13. use InvalidArgumentException;
  14. use JsonSerializable;
  15. use Traversable;
  16. use UnexpectedValueException;
  17. use UnitEnum;
  18. use WeakMap;
  19. /**
  20. * @template TKey of array-key
  21. *
  22. * @template-covariant TValue
  23. *
  24. * @property-read HigherOrderCollectionProxy $average
  25. * @property-read HigherOrderCollectionProxy $avg
  26. * @property-read HigherOrderCollectionProxy $contains
  27. * @property-read HigherOrderCollectionProxy $doesntContain
  28. * @property-read HigherOrderCollectionProxy $each
  29. * @property-read HigherOrderCollectionProxy $every
  30. * @property-read HigherOrderCollectionProxy $filter
  31. * @property-read HigherOrderCollectionProxy $first
  32. * @property-read HigherOrderCollectionProxy $flatMap
  33. * @property-read HigherOrderCollectionProxy $groupBy
  34. * @property-read HigherOrderCollectionProxy $keyBy
  35. * @property-read HigherOrderCollectionProxy $map
  36. * @property-read HigherOrderCollectionProxy $max
  37. * @property-read HigherOrderCollectionProxy $min
  38. * @property-read HigherOrderCollectionProxy $partition
  39. * @property-read HigherOrderCollectionProxy $percentage
  40. * @property-read HigherOrderCollectionProxy $reject
  41. * @property-read HigherOrderCollectionProxy $skipUntil
  42. * @property-read HigherOrderCollectionProxy $skipWhile
  43. * @property-read HigherOrderCollectionProxy $some
  44. * @property-read HigherOrderCollectionProxy $sortBy
  45. * @property-read HigherOrderCollectionProxy $sortByDesc
  46. * @property-read HigherOrderCollectionProxy $sum
  47. * @property-read HigherOrderCollectionProxy $takeUntil
  48. * @property-read HigherOrderCollectionProxy $takeWhile
  49. * @property-read HigherOrderCollectionProxy $unique
  50. * @property-read HigherOrderCollectionProxy $unless
  51. * @property-read HigherOrderCollectionProxy $until
  52. * @property-read HigherOrderCollectionProxy $when
  53. */
  54. trait EnumeratesValues
  55. {
  56. use Conditionable;
  57. /**
  58. * Indicates that the object's string representation should be escaped when __toString is invoked.
  59. *
  60. * @var bool
  61. */
  62. protected $escapeWhenCastingToString = false;
  63. /**
  64. * The methods that can be proxied.
  65. *
  66. * @var array<int, string>
  67. */
  68. protected static $proxies = [
  69. 'average',
  70. 'avg',
  71. 'contains',
  72. 'doesntContain',
  73. 'each',
  74. 'every',
  75. 'filter',
  76. 'first',
  77. 'flatMap',
  78. 'groupBy',
  79. 'keyBy',
  80. 'map',
  81. 'max',
  82. 'min',
  83. 'partition',
  84. 'percentage',
  85. 'reject',
  86. 'skipUntil',
  87. 'skipWhile',
  88. 'some',
  89. 'sortBy',
  90. 'sortByDesc',
  91. 'sum',
  92. 'takeUntil',
  93. 'takeWhile',
  94. 'unique',
  95. 'unless',
  96. 'until',
  97. 'when',
  98. ];
  99. /**
  100. * Create a new collection instance if the value isn't one already.
  101. *
  102. * @template TMakeKey of array-key
  103. * @template TMakeValue
  104. *
  105. * @param \Illuminate\Contracts\Support\Arrayable<TMakeKey, TMakeValue>|iterable<TMakeKey, TMakeValue>|null $items
  106. * @return static<TMakeKey, TMakeValue>
  107. */
  108. public static function make($items = [])
  109. {
  110. return new static($items);
  111. }
  112. /**
  113. * Wrap the given value in a collection if applicable.
  114. *
  115. * @template TWrapValue
  116. *
  117. * @param iterable<array-key, TWrapValue>|TWrapValue $value
  118. * @return static<array-key, TWrapValue>
  119. */
  120. public static function wrap($value)
  121. {
  122. return $value instanceof Enumerable
  123. ? new static($value)
  124. : new static(Arr::wrap($value));
  125. }
  126. /**
  127. * Get the underlying items from the given collection if applicable.
  128. *
  129. * @template TUnwrapKey of array-key
  130. * @template TUnwrapValue
  131. *
  132. * @param array<TUnwrapKey, TUnwrapValue>|static<TUnwrapKey, TUnwrapValue> $value
  133. * @return array<TUnwrapKey, TUnwrapValue>
  134. */
  135. public static function unwrap($value)
  136. {
  137. return $value instanceof Enumerable ? $value->all() : $value;
  138. }
  139. /**
  140. * Create a new instance with no items.
  141. *
  142. * @return static
  143. */
  144. public static function empty()
  145. {
  146. return new static([]);
  147. }
  148. /**
  149. * Create a new collection by invoking the callback a given amount of times.
  150. *
  151. * @template TTimesValue
  152. *
  153. * @param int $number
  154. * @param (callable(int): TTimesValue)|null $callback
  155. * @return static<int, TTimesValue>
  156. */
  157. public static function times($number, ?callable $callback = null)
  158. {
  159. if ($number < 1) {
  160. return new static;
  161. }
  162. return static::range(1, $number)
  163. ->unless($callback == null)
  164. ->map($callback);
  165. }
  166. /**
  167. * Get the average value of a given key.
  168. *
  169. * @param (callable(TValue): float|int)|string|null $callback
  170. * @return float|int|null
  171. */
  172. public function avg($callback = null)
  173. {
  174. $callback = $this->valueRetriever($callback);
  175. $reduced = $this->reduce(static function (&$reduce, $value) use ($callback) {
  176. if (! is_null($resolved = $callback($value))) {
  177. $reduce[0] += $resolved;
  178. $reduce[1]++;
  179. }
  180. return $reduce;
  181. }, [0, 0]);
  182. return $reduced[1] ? $reduced[0] / $reduced[1] : null;
  183. }
  184. /**
  185. * Alias for the "avg" method.
  186. *
  187. * @param (callable(TValue): float|int)|string|null $callback
  188. * @return float|int|null
  189. */
  190. public function average($callback = null)
  191. {
  192. return $this->avg($callback);
  193. }
  194. /**
  195. * Alias for the "contains" method.
  196. *
  197. * @param (callable(TValue, TKey): bool)|TValue|string $key
  198. * @param mixed $operator
  199. * @param mixed $value
  200. * @return bool
  201. */
  202. public function some($key, $operator = null, $value = null)
  203. {
  204. return $this->contains(...func_get_args());
  205. }
  206. /**
  207. * Dump the given arguments and terminate execution.
  208. *
  209. * @param mixed ...$args
  210. * @return never
  211. */
  212. public function dd(...$args)
  213. {
  214. $this->dump(...$args);
  215. dd();
  216. }
  217. /**
  218. * Dump the items.
  219. *
  220. * @param mixed ...$args
  221. * @return $this
  222. */
  223. public function dump(...$args)
  224. {
  225. dump($this->all(), ...$args);
  226. return $this;
  227. }
  228. /**
  229. * Execute a callback over each item.
  230. *
  231. * @param callable(TValue, TKey): mixed $callback
  232. * @return $this
  233. */
  234. public function each(callable $callback)
  235. {
  236. foreach ($this as $key => $item) {
  237. if ($callback($item, $key) === false) {
  238. break;
  239. }
  240. }
  241. return $this;
  242. }
  243. /**
  244. * Execute a callback over each nested chunk of items.
  245. *
  246. * @param callable(...mixed): mixed $callback
  247. * @return static
  248. */
  249. public function eachSpread(callable $callback)
  250. {
  251. return $this->each(function ($chunk, $key) use ($callback) {
  252. $chunk[] = $key;
  253. return $callback(...$chunk);
  254. });
  255. }
  256. /**
  257. * Determine if all items pass the given truth test.
  258. *
  259. * @param (callable(TValue, TKey): bool)|TValue|string $key
  260. * @param mixed $operator
  261. * @param mixed $value
  262. * @return bool
  263. */
  264. public function every($key, $operator = null, $value = null)
  265. {
  266. if (func_num_args() === 1) {
  267. $callback = $this->valueRetriever($key);
  268. foreach ($this as $k => $v) {
  269. if (! $callback($v, $k)) {
  270. return false;
  271. }
  272. }
  273. return true;
  274. }
  275. return $this->every($this->operatorForWhere(...func_get_args()));
  276. }
  277. /**
  278. * Get the first item by the given key value pair.
  279. *
  280. * @param callable|string $key
  281. * @param mixed $operator
  282. * @param mixed $value
  283. * @return TValue|null
  284. */
  285. public function firstWhere($key, $operator = null, $value = null)
  286. {
  287. return $this->first($this->operatorForWhere(...func_get_args()));
  288. }
  289. /**
  290. * Get a single key's value from the first matching item in the collection.
  291. *
  292. * @template TValueDefault
  293. *
  294. * @param string $key
  295. * @param TValueDefault|(\Closure(): TValueDefault) $default
  296. * @return TValue|TValueDefault
  297. */
  298. public function value($key, $default = null)
  299. {
  300. if ($value = $this->firstWhere($key)) {
  301. return data_get($value, $key, $default);
  302. }
  303. return value($default);
  304. }
  305. /**
  306. * Ensure that every item in the collection is of the expected type.
  307. *
  308. * @template TEnsureOfType
  309. *
  310. * @param class-string<TEnsureOfType>|array<array-key, class-string<TEnsureOfType>> $type
  311. * @return static<TKey, TEnsureOfType>
  312. *
  313. * @throws \UnexpectedValueException
  314. */
  315. public function ensure($type)
  316. {
  317. $allowedTypes = is_array($type) ? $type : [$type];
  318. return $this->each(function ($item, $index) use ($allowedTypes) {
  319. $itemType = get_debug_type($item);
  320. foreach ($allowedTypes as $allowedType) {
  321. if ($itemType === $allowedType || $item instanceof $allowedType) {
  322. return true;
  323. }
  324. }
  325. throw new UnexpectedValueException(
  326. sprintf("Collection should only include [%s] items, but '%s' found at position %d.", implode(', ', $allowedTypes), $itemType, $index)
  327. );
  328. });
  329. }
  330. /**
  331. * Determine if the collection is not empty.
  332. *
  333. * @return bool
  334. */
  335. public function isNotEmpty()
  336. {
  337. return ! $this->isEmpty();
  338. }
  339. /**
  340. * Run a map over each nested chunk of items.
  341. *
  342. * @template TMapSpreadValue
  343. *
  344. * @param callable(mixed...): TMapSpreadValue $callback
  345. * @return static<TKey, TMapSpreadValue>
  346. */
  347. public function mapSpread(callable $callback)
  348. {
  349. return $this->map(function ($chunk, $key) use ($callback) {
  350. $chunk[] = $key;
  351. return $callback(...$chunk);
  352. });
  353. }
  354. /**
  355. * Run a grouping map over the items.
  356. *
  357. * The callback should return an associative array with a single key/value pair.
  358. *
  359. * @template TMapToGroupsKey of array-key
  360. * @template TMapToGroupsValue
  361. *
  362. * @param callable(TValue, TKey): array<TMapToGroupsKey, TMapToGroupsValue> $callback
  363. * @return static<TMapToGroupsKey, static<int, TMapToGroupsValue>>
  364. */
  365. public function mapToGroups(callable $callback)
  366. {
  367. $groups = $this->mapToDictionary($callback);
  368. return $groups->map([$this, 'make']);
  369. }
  370. /**
  371. * Map a collection and flatten the result by a single level.
  372. *
  373. * @template TFlatMapKey of array-key
  374. * @template TFlatMapValue
  375. *
  376. * @param callable(TValue, TKey): (\Illuminate\Support\Collection<TFlatMapKey, TFlatMapValue>|array<TFlatMapKey, TFlatMapValue>) $callback
  377. * @return static<TFlatMapKey, TFlatMapValue>
  378. */
  379. public function flatMap(callable $callback)
  380. {
  381. return $this->map($callback)->collapse();
  382. }
  383. /**
  384. * Map the values into a new class.
  385. *
  386. * @template TMapIntoValue
  387. *
  388. * @param class-string<TMapIntoValue> $class
  389. * @return static<TKey, TMapIntoValue>
  390. */
  391. public function mapInto($class)
  392. {
  393. if (is_subclass_of($class, BackedEnum::class)) {
  394. return $this->map(fn ($value, $key) => $class::from($value));
  395. }
  396. return $this->map(fn ($value, $key) => new $class($value, $key));
  397. }
  398. /**
  399. * Get the min value of a given key.
  400. *
  401. * @param (callable(TValue):mixed)|string|null $callback
  402. * @return mixed
  403. */
  404. public function min($callback = null)
  405. {
  406. $callback = $this->valueRetriever($callback);
  407. return $this->map(fn ($value) => $callback($value))
  408. ->filter(fn ($value) => ! is_null($value))
  409. ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result);
  410. }
  411. /**
  412. * Get the max value of a given key.
  413. *
  414. * @param (callable(TValue):mixed)|string|null $callback
  415. * @return mixed
  416. */
  417. public function max($callback = null)
  418. {
  419. $callback = $this->valueRetriever($callback);
  420. return $this->filter(fn ($value) => ! is_null($value))->reduce(function ($result, $item) use ($callback) {
  421. $value = $callback($item);
  422. return is_null($result) || $value > $result ? $value : $result;
  423. });
  424. }
  425. /**
  426. * "Paginate" the collection by slicing it into a smaller collection.
  427. *
  428. * @param int $page
  429. * @param int $perPage
  430. * @return static
  431. */
  432. public function forPage($page, $perPage)
  433. {
  434. $offset = max(0, ($page - 1) * $perPage);
  435. return $this->slice($offset, $perPage);
  436. }
  437. /**
  438. * Partition the collection into two arrays using the given callback or key.
  439. *
  440. * @param (callable(TValue, TKey): bool)|TValue|string $key
  441. * @param TValue|string|null $operator
  442. * @param TValue|null $value
  443. * @return static<int<0, 1>, static<TKey, TValue>>
  444. */
  445. public function partition($key, $operator = null, $value = null)
  446. {
  447. $passed = [];
  448. $failed = [];
  449. $callback = func_num_args() === 1
  450. ? $this->valueRetriever($key)
  451. : $this->operatorForWhere(...func_get_args());
  452. foreach ($this as $key => $item) {
  453. if ($callback($item, $key)) {
  454. $passed[$key] = $item;
  455. } else {
  456. $failed[$key] = $item;
  457. }
  458. }
  459. return new static([new static($passed), new static($failed)]);
  460. }
  461. /**
  462. * Calculate the percentage of items that pass a given truth test.
  463. *
  464. * @param (callable(TValue, TKey): bool) $callback
  465. * @param int $precision
  466. * @return float|null
  467. */
  468. public function percentage(callable $callback, int $precision = 2)
  469. {
  470. if ($this->isEmpty()) {
  471. return null;
  472. }
  473. return round(
  474. $this->filter($callback)->count() / $this->count() * 100,
  475. $precision
  476. );
  477. }
  478. /**
  479. * Get the sum of the given values.
  480. *
  481. * @param (callable(TValue): mixed)|string|null $callback
  482. * @return mixed
  483. */
  484. public function sum($callback = null)
  485. {
  486. $callback = is_null($callback)
  487. ? $this->identity()
  488. : $this->valueRetriever($callback);
  489. return $this->reduce(fn ($result, $item) => $result + $callback($item), 0);
  490. }
  491. /**
  492. * Apply the callback if the collection is empty.
  493. *
  494. * @template TWhenEmptyReturnType
  495. *
  496. * @param (callable($this): TWhenEmptyReturnType) $callback
  497. * @param (callable($this): TWhenEmptyReturnType)|null $default
  498. * @return $this|TWhenEmptyReturnType
  499. */
  500. public function whenEmpty(callable $callback, ?callable $default = null)
  501. {
  502. return $this->when($this->isEmpty(), $callback, $default);
  503. }
  504. /**
  505. * Apply the callback if the collection is not empty.
  506. *
  507. * @template TWhenNotEmptyReturnType
  508. *
  509. * @param callable($this): TWhenNotEmptyReturnType $callback
  510. * @param (callable($this): TWhenNotEmptyReturnType)|null $default
  511. * @return $this|TWhenNotEmptyReturnType
  512. */
  513. public function whenNotEmpty(callable $callback, ?callable $default = null)
  514. {
  515. return $this->when($this->isNotEmpty(), $callback, $default);
  516. }
  517. /**
  518. * Apply the callback unless the collection is empty.
  519. *
  520. * @template TUnlessEmptyReturnType
  521. *
  522. * @param callable($this): TUnlessEmptyReturnType $callback
  523. * @param (callable($this): TUnlessEmptyReturnType)|null $default
  524. * @return $this|TUnlessEmptyReturnType
  525. */
  526. public function unlessEmpty(callable $callback, ?callable $default = null)
  527. {
  528. return $this->whenNotEmpty($callback, $default);
  529. }
  530. /**
  531. * Apply the callback unless the collection is not empty.
  532. *
  533. * @template TUnlessNotEmptyReturnType
  534. *
  535. * @param callable($this): TUnlessNotEmptyReturnType $callback
  536. * @param (callable($this): TUnlessNotEmptyReturnType)|null $default
  537. * @return $this|TUnlessNotEmptyReturnType
  538. */
  539. public function unlessNotEmpty(callable $callback, ?callable $default = null)
  540. {
  541. return $this->whenEmpty($callback, $default);
  542. }
  543. /**
  544. * Filter items by the given key value pair.
  545. *
  546. * @param callable|string $key
  547. * @param mixed $operator
  548. * @param mixed $value
  549. * @return static
  550. */
  551. public function where($key, $operator = null, $value = null)
  552. {
  553. return $this->filter($this->operatorForWhere(...func_get_args()));
  554. }
  555. /**
  556. * Filter items where the value for the given key is null.
  557. *
  558. * @param string|null $key
  559. * @return static
  560. */
  561. public function whereNull($key = null)
  562. {
  563. return $this->whereStrict($key, null);
  564. }
  565. /**
  566. * Filter items where the value for the given key is not null.
  567. *
  568. * @param string|null $key
  569. * @return static
  570. */
  571. public function whereNotNull($key = null)
  572. {
  573. return $this->where($key, '!==', null);
  574. }
  575. /**
  576. * Filter items by the given key value pair using strict comparison.
  577. *
  578. * @param string $key
  579. * @param mixed $value
  580. * @return static
  581. */
  582. public function whereStrict($key, $value)
  583. {
  584. return $this->where($key, '===', $value);
  585. }
  586. /**
  587. * Filter items by the given key value pair.
  588. *
  589. * @param string $key
  590. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  591. * @param bool $strict
  592. * @return static
  593. */
  594. public function whereIn($key, $values, $strict = false)
  595. {
  596. $values = $this->getArrayableItems($values);
  597. return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict));
  598. }
  599. /**
  600. * Filter items by the given key value pair using strict comparison.
  601. *
  602. * @param string $key
  603. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  604. * @return static
  605. */
  606. public function whereInStrict($key, $values)
  607. {
  608. return $this->whereIn($key, $values, true);
  609. }
  610. /**
  611. * Filter items such that the value of the given key is between the given values.
  612. *
  613. * @param string $key
  614. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  615. * @return static
  616. */
  617. public function whereBetween($key, $values)
  618. {
  619. return $this->where($key, '>=', reset($values))->where($key, '<=', end($values));
  620. }
  621. /**
  622. * Filter items such that the value of the given key is not between the given values.
  623. *
  624. * @param string $key
  625. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  626. * @return static
  627. */
  628. public function whereNotBetween($key, $values)
  629. {
  630. return $this->filter(
  631. fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values)
  632. );
  633. }
  634. /**
  635. * Filter items by the given key value pair.
  636. *
  637. * @param string $key
  638. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  639. * @param bool $strict
  640. * @return static
  641. */
  642. public function whereNotIn($key, $values, $strict = false)
  643. {
  644. $values = $this->getArrayableItems($values);
  645. return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict));
  646. }
  647. /**
  648. * Filter items by the given key value pair using strict comparison.
  649. *
  650. * @param string $key
  651. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  652. * @return static
  653. */
  654. public function whereNotInStrict($key, $values)
  655. {
  656. return $this->whereNotIn($key, $values, true);
  657. }
  658. /**
  659. * Filter the items, removing any items that don't match the given type(s).
  660. *
  661. * @template TWhereInstanceOf
  662. *
  663. * @param class-string<TWhereInstanceOf>|array<array-key, class-string<TWhereInstanceOf>> $type
  664. * @return static<TKey, TWhereInstanceOf>
  665. */
  666. public function whereInstanceOf($type)
  667. {
  668. return $this->filter(function ($value) use ($type) {
  669. if (is_array($type)) {
  670. foreach ($type as $classType) {
  671. if ($value instanceof $classType) {
  672. return true;
  673. }
  674. }
  675. return false;
  676. }
  677. return $value instanceof $type;
  678. });
  679. }
  680. /**
  681. * Pass the collection to the given callback and return the result.
  682. *
  683. * @template TPipeReturnType
  684. *
  685. * @param callable($this): TPipeReturnType $callback
  686. * @return TPipeReturnType
  687. */
  688. public function pipe(callable $callback)
  689. {
  690. return $callback($this);
  691. }
  692. /**
  693. * Pass the collection into a new class.
  694. *
  695. * @template TPipeIntoValue
  696. *
  697. * @param class-string<TPipeIntoValue> $class
  698. * @return TPipeIntoValue
  699. */
  700. public function pipeInto($class)
  701. {
  702. return new $class($this);
  703. }
  704. /**
  705. * Pass the collection through a series of callable pipes and return the result.
  706. *
  707. * @param array<callable> $callbacks
  708. * @return mixed
  709. */
  710. public function pipeThrough($callbacks)
  711. {
  712. return Collection::make($callbacks)->reduce(
  713. fn ($carry, $callback) => $callback($carry),
  714. $this,
  715. );
  716. }
  717. /**
  718. * Reduce the collection to a single value.
  719. *
  720. * @template TReduceInitial
  721. * @template TReduceReturnType
  722. *
  723. * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback
  724. * @param TReduceInitial $initial
  725. * @return TReduceReturnType
  726. */
  727. public function reduce(callable $callback, $initial = null)
  728. {
  729. $result = $initial;
  730. foreach ($this as $key => $value) {
  731. $result = $callback($result, $value, $key);
  732. }
  733. return $result;
  734. }
  735. /**
  736. * Reduce the collection to multiple aggregate values.
  737. *
  738. * @param callable $callback
  739. * @param mixed ...$initial
  740. * @return array
  741. *
  742. * @throws \UnexpectedValueException
  743. */
  744. public function reduceSpread(callable $callback, ...$initial)
  745. {
  746. $result = $initial;
  747. foreach ($this as $key => $value) {
  748. $result = call_user_func_array($callback, array_merge($result, [$value, $key]));
  749. if (! is_array($result)) {
  750. throw new UnexpectedValueException(sprintf(
  751. "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.",
  752. class_basename(static::class), gettype($result)
  753. ));
  754. }
  755. }
  756. return $result;
  757. }
  758. /**
  759. * Reduce an associative collection to a single value.
  760. *
  761. * @template TReduceWithKeysInitial
  762. * @template TReduceWithKeysReturnType
  763. *
  764. * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback
  765. * @param TReduceWithKeysInitial $initial
  766. * @return TReduceWithKeysReturnType
  767. */
  768. public function reduceWithKeys(callable $callback, $initial = null)
  769. {
  770. return $this->reduce($callback, $initial);
  771. }
  772. /**
  773. * Create a collection of all elements that do not pass a given truth test.
  774. *
  775. * @param (callable(TValue, TKey): bool)|bool|TValue $callback
  776. * @return static
  777. */
  778. public function reject($callback = true)
  779. {
  780. $useAsCallable = $this->useAsCallable($callback);
  781. return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
  782. return $useAsCallable
  783. ? ! $callback($value, $key)
  784. : $value != $callback;
  785. });
  786. }
  787. /**
  788. * Pass the collection to the given callback and then return it.
  789. *
  790. * @param callable($this): mixed $callback
  791. * @return $this
  792. */
  793. public function tap(callable $callback)
  794. {
  795. $callback($this);
  796. return $this;
  797. }
  798. /**
  799. * Return only unique items from the collection array.
  800. *
  801. * @param (callable(TValue, TKey): mixed)|string|null $key
  802. * @param bool $strict
  803. * @return static
  804. */
  805. public function unique($key = null, $strict = false)
  806. {
  807. $callback = $this->valueRetriever($key);
  808. $exists = [];
  809. return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
  810. if (in_array($id = $callback($item, $key), $exists, $strict)) {
  811. return true;
  812. }
  813. $exists[] = $id;
  814. });
  815. }
  816. /**
  817. * Return only unique items from the collection array using strict comparison.
  818. *
  819. * @param (callable(TValue, TKey): mixed)|string|null $key
  820. * @return static
  821. */
  822. public function uniqueStrict($key = null)
  823. {
  824. return $this->unique($key, true);
  825. }
  826. /**
  827. * Collect the values into a collection.
  828. *
  829. * @return \Illuminate\Support\Collection<TKey, TValue>
  830. */
  831. public function collect()
  832. {
  833. return new Collection($this->all());
  834. }
  835. /**
  836. * Get the collection of items as a plain array.
  837. *
  838. * @return array<TKey, mixed>
  839. */
  840. public function toArray()
  841. {
  842. return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all();
  843. }
  844. /**
  845. * Convert the object into something JSON serializable.
  846. *
  847. * @return array<TKey, mixed>
  848. */
  849. public function jsonSerialize(): array
  850. {
  851. return array_map(function ($value) {
  852. if ($value instanceof JsonSerializable) {
  853. return $value->jsonSerialize();
  854. } elseif ($value instanceof Jsonable) {
  855. return json_decode($value->toJson(), true);
  856. } elseif ($value instanceof Arrayable) {
  857. return $value->toArray();
  858. }
  859. return $value;
  860. }, $this->all());
  861. }
  862. /**
  863. * Get the collection of items as JSON.
  864. *
  865. * @param int $options
  866. * @return string
  867. */
  868. public function toJson($options = 0)
  869. {
  870. return json_encode($this->jsonSerialize(), $options);
  871. }
  872. /**
  873. * Get a CachingIterator instance.
  874. *
  875. * @param int $flags
  876. * @return \CachingIterator
  877. */
  878. public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
  879. {
  880. return new CachingIterator($this->getIterator(), $flags);
  881. }
  882. /**
  883. * Convert the collection to its string representation.
  884. *
  885. * @return string
  886. */
  887. public function __toString()
  888. {
  889. return $this->escapeWhenCastingToString
  890. ? e($this->toJson())
  891. : $this->toJson();
  892. }
  893. /**
  894. * Indicate that the model's string representation should be escaped when __toString is invoked.
  895. *
  896. * @param bool $escape
  897. * @return $this
  898. */
  899. public function escapeWhenCastingToString($escape = true)
  900. {
  901. $this->escapeWhenCastingToString = $escape;
  902. return $this;
  903. }
  904. /**
  905. * Add a method to the list of proxied methods.
  906. *
  907. * @param string $method
  908. * @return void
  909. */
  910. public static function proxy($method)
  911. {
  912. static::$proxies[] = $method;
  913. }
  914. /**
  915. * Dynamically access collection proxies.
  916. *
  917. * @param string $key
  918. * @return mixed
  919. *
  920. * @throws \Exception
  921. */
  922. public function __get($key)
  923. {
  924. if (! in_array($key, static::$proxies)) {
  925. throw new Exception("Property [{$key}] does not exist on this collection instance.");
  926. }
  927. return new HigherOrderCollectionProxy($this, $key);
  928. }
  929. /**
  930. * Results array of items from Collection or Arrayable.
  931. *
  932. * @param mixed $items
  933. * @return array<TKey, TValue>
  934. */
  935. protected function getArrayableItems($items)
  936. {
  937. if (is_array($items)) {
  938. return $items;
  939. }
  940. return match (true) {
  941. $items instanceof WeakMap => throw new InvalidArgumentException('Collections can not be created using instances of WeakMap.'),
  942. $items instanceof Enumerable => $items->all(),
  943. $items instanceof Arrayable => $items->toArray(),
  944. $items instanceof Traversable => iterator_to_array($items),
  945. $items instanceof Jsonable => json_decode($items->toJson(), true),
  946. $items instanceof JsonSerializable => (array) $items->jsonSerialize(),
  947. $items instanceof UnitEnum => [$items],
  948. default => (array) $items,
  949. };
  950. }
  951. /**
  952. * Get an operator checker callback.
  953. *
  954. * @param callable|string $key
  955. * @param string|null $operator
  956. * @param mixed $value
  957. * @return \Closure
  958. */
  959. protected function operatorForWhere($key, $operator = null, $value = null)
  960. {
  961. if ($this->useAsCallable($key)) {
  962. return $key;
  963. }
  964. if (func_num_args() === 1) {
  965. $value = true;
  966. $operator = '=';
  967. }
  968. if (func_num_args() === 2) {
  969. $value = $operator;
  970. $operator = '=';
  971. }
  972. return function ($item) use ($key, $operator, $value) {
  973. $retrieved = data_get($item, $key);
  974. $strings = array_filter([$retrieved, $value], function ($value) {
  975. return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
  976. });
  977. if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
  978. return in_array($operator, ['!=', '<>', '!==']);
  979. }
  980. switch ($operator) {
  981. default:
  982. case '=':
  983. case '==': return $retrieved == $value;
  984. case '!=':
  985. case '<>': return $retrieved != $value;
  986. case '<': return $retrieved < $value;
  987. case '>': return $retrieved > $value;
  988. case '<=': return $retrieved <= $value;
  989. case '>=': return $retrieved >= $value;
  990. case '===': return $retrieved === $value;
  991. case '!==': return $retrieved !== $value;
  992. case '<=>': return $retrieved <=> $value;
  993. }
  994. };
  995. }
  996. /**
  997. * Determine if the given value is callable, but not a string.
  998. *
  999. * @param mixed $value
  1000. * @return bool
  1001. */
  1002. protected function useAsCallable($value)
  1003. {
  1004. return ! is_string($value) && is_callable($value);
  1005. }
  1006. /**
  1007. * Get a value retrieving callback.
  1008. *
  1009. * @param callable|string|null $value
  1010. * @return callable
  1011. */
  1012. protected function valueRetriever($value)
  1013. {
  1014. if ($this->useAsCallable($value)) {
  1015. return $value;
  1016. }
  1017. return fn ($item) => data_get($item, $value);
  1018. }
  1019. /**
  1020. * Make a function to check an item's equality.
  1021. *
  1022. * @param mixed $value
  1023. * @return \Closure(mixed): bool
  1024. */
  1025. protected function equality($value)
  1026. {
  1027. return fn ($item) => $item === $value;
  1028. }
  1029. /**
  1030. * Make a function using another function, by negating its result.
  1031. *
  1032. * @param \Closure $callback
  1033. * @return \Closure
  1034. */
  1035. protected function negate(Closure $callback)
  1036. {
  1037. return fn (...$params) => ! $callback(...$params);
  1038. }
  1039. /**
  1040. * Make a function that returns what's passed to it.
  1041. *
  1042. * @return \Closure(TValue): TValue
  1043. */
  1044. protected function identity()
  1045. {
  1046. return fn ($value) => $value;
  1047. }
  1048. }