EnumeratesValues.php 32 KB

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