Collection.php 42 KB

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