CollectionTest.php 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Collection;
  16. use ArrayIterator;
  17. use ArrayObject;
  18. use Cake\Collection\Collection;
  19. use Cake\Collection\CollectionInterface;
  20. use Cake\Collection\CollectionTrait;
  21. use Cake\TestSuite\TestCase;
  22. use NoRewindIterator;
  23. class TestCollection extends \IteratorIterator implements CollectionInterface
  24. {
  25. use CollectionTrait;
  26. public function __construct($items)
  27. {
  28. if (is_array($items)) {
  29. $items = new \ArrayIterator($items);
  30. }
  31. if (!($items instanceof \Traversable)) {
  32. $msg = 'Only an array or \Traversable is allowed for Collection';
  33. throw new \InvalidArgumentException($msg);
  34. }
  35. parent::__construct($items);
  36. }
  37. }
  38. /**
  39. * CollectionTest
  40. *
  41. */
  42. class CollectionTest extends TestCase
  43. {
  44. /**
  45. * Tests that it is possible to convert an array into a collection
  46. *
  47. * @return void
  48. */
  49. public function testArrayIsWrapped()
  50. {
  51. $items = [1, 2, 3];
  52. $collection = new Collection($items);
  53. $this->assertEquals($items, iterator_to_array($collection));
  54. }
  55. /**
  56. * Tests that it is possible to convert an iterator into a collection
  57. *
  58. * @return void
  59. */
  60. public function testIteratorIsWrapped()
  61. {
  62. $items = new \ArrayObject([1, 2, 3]);
  63. $collection = new Collection($items);
  64. $this->assertEquals(iterator_to_array($items), iterator_to_array($collection));
  65. }
  66. /**
  67. * Test running a method over all elements in the collection
  68. *
  69. * @return void
  70. */
  71. public function testEeach()
  72. {
  73. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  74. $collection = new Collection($items);
  75. $callable = $this->getMock('stdClass', ['__invoke']);
  76. $callable->expects($this->at(0))
  77. ->method('__invoke')
  78. ->with(1, 'a');
  79. $callable->expects($this->at(1))
  80. ->method('__invoke')
  81. ->with(2, 'b');
  82. $callable->expects($this->at(2))
  83. ->method('__invoke')
  84. ->with(3, 'c');
  85. $collection->each($callable);
  86. }
  87. /**
  88. * Test filter() with no callback.
  89. *
  90. * @return void
  91. */
  92. public function testFilterNoCallback()
  93. {
  94. $items = [1, 2, 0, 3, false, 4, null, 5, ''];
  95. $collection = new Collection($items);
  96. $result = $collection->filter()->toArray();
  97. $expected = [1, 2, 3, 4, 5];
  98. $this->assertEquals($expected, array_values($result));
  99. }
  100. /**
  101. * Tests that it is possible to chain filter() as it returns a collection object
  102. *
  103. * @return void
  104. */
  105. public function testFilterChaining()
  106. {
  107. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  108. $collection = new Collection($items);
  109. $callable = $this->getMock('stdClass', ['__invoke']);
  110. $callable->expects($this->once())
  111. ->method('__invoke')
  112. ->with(3, 'c');
  113. $filtered = $collection->filter(function ($value, $key, $iterator) {
  114. return $value > 2;
  115. });
  116. $this->assertInstanceOf('Cake\Collection\Collection', $filtered);
  117. $filtered->each($callable);
  118. }
  119. /**
  120. * Tests reject
  121. *
  122. * @return void
  123. */
  124. public function testReject()
  125. {
  126. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  127. $collection = new Collection($items);
  128. $result = $collection->reject(function ($v, $k, $items) use ($collection) {
  129. $this->assertSame($collection->getInnerIterator(), $items);
  130. return $v > 2;
  131. });
  132. $this->assertEquals(['a' => 1, 'b' => 2], iterator_to_array($result));
  133. $this->assertInstanceOf('Cake\Collection\Collection', $result);
  134. }
  135. /**
  136. * Tests every when the callback returns true for all elements
  137. *
  138. * @return void
  139. */
  140. public function testEveryReturnTrue()
  141. {
  142. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  143. $collection = new Collection($items);
  144. $callable = $this->getMock('stdClass', ['__invoke']);
  145. $callable->expects($this->at(0))
  146. ->method('__invoke')
  147. ->with(1, 'a')
  148. ->will($this->returnValue(true));
  149. $callable->expects($this->at(1))
  150. ->method('__invoke')
  151. ->with(2, 'b')
  152. ->will($this->returnValue(true));
  153. $callable->expects($this->at(2))
  154. ->method('__invoke')
  155. ->with(3, 'c')
  156. ->will($this->returnValue(true));
  157. $this->assertTrue($collection->every($callable));
  158. }
  159. /**
  160. * Tests every when the callback returns false for one of the elements
  161. *
  162. * @return void
  163. */
  164. public function testEveryReturnFalse()
  165. {
  166. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  167. $collection = new Collection($items);
  168. $callable = $this->getMock('stdClass', ['__invoke']);
  169. $callable->expects($this->at(0))
  170. ->method('__invoke')
  171. ->with(1, 'a')
  172. ->will($this->returnValue(true));
  173. $callable->expects($this->at(1))
  174. ->method('__invoke')
  175. ->with(2, 'b')
  176. ->will($this->returnValue(false));
  177. $callable->expects($this->exactly(2))->method('__invoke');
  178. $this->assertFalse($collection->every($callable));
  179. }
  180. /**
  181. * Tests some() when one of the calls return true
  182. *
  183. * @return void
  184. */
  185. public function testSomeReturnTrue()
  186. {
  187. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  188. $collection = new Collection($items);
  189. $callable = $this->getMock('stdClass', ['__invoke']);
  190. $callable->expects($this->at(0))
  191. ->method('__invoke')
  192. ->with(1, 'a')
  193. ->will($this->returnValue(false));
  194. $callable->expects($this->at(1))
  195. ->method('__invoke')
  196. ->with(2, 'b')
  197. ->will($this->returnValue(true));
  198. $callable->expects($this->exactly(2))->method('__invoke');
  199. $this->assertTrue($collection->some($callable));
  200. }
  201. /**
  202. * Tests some() when none of the calls return true
  203. *
  204. * @return void
  205. */
  206. public function testSomeReturnFalse()
  207. {
  208. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  209. $collection = new Collection($items);
  210. $callable = $this->getMock('stdClass', ['__invoke']);
  211. $callable->expects($this->at(0))
  212. ->method('__invoke')
  213. ->with(1, 'a')
  214. ->will($this->returnValue(false));
  215. $callable->expects($this->at(1))
  216. ->method('__invoke')
  217. ->with(2, 'b')
  218. ->will($this->returnValue(false));
  219. $callable->expects($this->at(2))
  220. ->method('__invoke')
  221. ->with(3, 'c')
  222. ->will($this->returnValue(false));
  223. $this->assertFalse($collection->some($callable));
  224. }
  225. /**
  226. * Tests contains
  227. *
  228. * @return void
  229. */
  230. public function testContains()
  231. {
  232. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  233. $collection = new Collection($items);
  234. $this->assertTrue($collection->contains(2));
  235. $this->assertTrue($collection->contains(1));
  236. $this->assertFalse($collection->contains(10));
  237. $this->assertFalse($collection->contains('2'));
  238. }
  239. /**
  240. * Tests map
  241. *
  242. * @return void
  243. */
  244. public function testMap()
  245. {
  246. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  247. $collection = new Collection($items);
  248. $map = $collection->map(function ($v, $k, $it) use ($collection) {
  249. $this->assertSame($collection->getInnerIterator(), $it);
  250. return $v * $v;
  251. });
  252. $this->assertInstanceOf('Cake\Collection\Iterator\ReplaceIterator', $map);
  253. $this->assertEquals(['a' => 1, 'b' => 4, 'c' => 9], iterator_to_array($map));
  254. }
  255. /**
  256. * Tests reduce with initial value
  257. *
  258. * @return void
  259. */
  260. public function testReduceWithInitialValue()
  261. {
  262. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  263. $collection = new Collection($items);
  264. $callable = $this->getMock('stdClass', ['__invoke']);
  265. $callable->expects($this->at(0))
  266. ->method('__invoke')
  267. ->with(10, 1, 'a')
  268. ->will($this->returnValue(11));
  269. $callable->expects($this->at(1))
  270. ->method('__invoke')
  271. ->with(11, 2, 'b')
  272. ->will($this->returnValue(13));
  273. $callable->expects($this->at(2))
  274. ->method('__invoke')
  275. ->with(13, 3, 'c')
  276. ->will($this->returnValue(16));
  277. $this->assertEquals(16, $collection->reduce($callable, 10));
  278. }
  279. /**
  280. * Tests reduce without initial value
  281. *
  282. * @return void
  283. */
  284. public function testReduceWithoutInitialValue()
  285. {
  286. $items = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4];
  287. $collection = new Collection($items);
  288. $callable = $this->getMock('stdClass', ['__invoke']);
  289. $callable->expects($this->at(0))
  290. ->method('__invoke')
  291. ->with(1, 2, 'b')
  292. ->will($this->returnValue(3));
  293. $callable->expects($this->at(1))
  294. ->method('__invoke')
  295. ->with(3, 3, 'c')
  296. ->will($this->returnValue(6));
  297. $callable->expects($this->at(2))
  298. ->method('__invoke')
  299. ->with(6, 4, 'd')
  300. ->will($this->returnValue(10));
  301. $this->assertEquals(10, $collection->reduce($callable));
  302. }
  303. /**
  304. * Tests extract
  305. *
  306. * @return void
  307. */
  308. public function testExtract()
  309. {
  310. $items = [['a' => ['b' => ['c' => 1]]], 2];
  311. $collection = new Collection($items);
  312. $map = $collection->extract('a.b.c');
  313. $this->assertInstanceOf('Cake\Collection\Iterator\ExtractIterator', $map);
  314. $this->assertEquals([1, null], iterator_to_array($map));
  315. }
  316. /**
  317. * Tests sort
  318. *
  319. * @return void
  320. */
  321. public function testSortString()
  322. {
  323. $items = [
  324. ['a' => ['b' => ['c' => 4]]],
  325. ['a' => ['b' => ['c' => 10]]],
  326. ['a' => ['b' => ['c' => 6]]]
  327. ];
  328. $collection = new Collection($items);
  329. $map = $collection->sortBy('a.b.c');
  330. $this->assertInstanceOf('Cake\Collection\Collection', $map);
  331. $expected = [
  332. ['a' => ['b' => ['c' => 10]]],
  333. ['a' => ['b' => ['c' => 6]]],
  334. ['a' => ['b' => ['c' => 4]]],
  335. ];
  336. $this->assertEquals($expected, $map->toList());
  337. }
  338. /**
  339. * Tests max
  340. *
  341. * @return void
  342. */
  343. public function testMax()
  344. {
  345. $items = [
  346. ['a' => ['b' => ['c' => 4]]],
  347. ['a' => ['b' => ['c' => 10]]],
  348. ['a' => ['b' => ['c' => 6]]]
  349. ];
  350. $collection = new Collection($items);
  351. $this->assertEquals(['a' => ['b' => ['c' => 10]]], $collection->max('a.b.c'));
  352. $callback = function ($e) {
  353. return $e['a']['b']['c'] * - 1;
  354. };
  355. $this->assertEquals(['a' => ['b' => ['c' => 4]]], $collection->max($callback));
  356. }
  357. /**
  358. * Tests min
  359. *
  360. * @return void
  361. */
  362. public function testMin()
  363. {
  364. $items = [
  365. ['a' => ['b' => ['c' => 4]]],
  366. ['a' => ['b' => ['c' => 10]]],
  367. ['a' => ['b' => ['c' => 6]]]
  368. ];
  369. $collection = new Collection($items);
  370. $this->assertEquals(['a' => ['b' => ['c' => 4]]], $collection->min('a.b.c'));
  371. }
  372. /**
  373. * Tests groupBy
  374. *
  375. * @return void
  376. */
  377. public function testGroupBy()
  378. {
  379. $items = [
  380. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  381. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  382. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  383. ];
  384. $collection = new Collection($items);
  385. $grouped = $collection->groupBy('parent_id');
  386. $expected = [
  387. 10 => [
  388. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  389. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  390. ],
  391. 11 => [
  392. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  393. ]
  394. ];
  395. $this->assertEquals($expected, iterator_to_array($grouped));
  396. $this->assertInstanceOf('Cake\Collection\Collection', $grouped);
  397. $grouped = $collection->groupBy(function ($element) {
  398. return $element['parent_id'];
  399. });
  400. $this->assertEquals($expected, iterator_to_array($grouped));
  401. }
  402. /**
  403. * Tests grouping by a deep key
  404. *
  405. * @return void
  406. */
  407. public function testGroupByDeepKey()
  408. {
  409. $items = [
  410. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  411. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  412. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  413. ];
  414. $collection = new Collection($items);
  415. $grouped = $collection->groupBy('thing.parent_id');
  416. $expected = [
  417. 10 => [
  418. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  419. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  420. ],
  421. 11 => [
  422. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  423. ]
  424. ];
  425. $this->assertEquals($expected, iterator_to_array($grouped));
  426. }
  427. /**
  428. * Tests indexBy
  429. *
  430. * @return void
  431. */
  432. public function testIndexBy()
  433. {
  434. $items = [
  435. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  436. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  437. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  438. ];
  439. $collection = new Collection($items);
  440. $grouped = $collection->indexBy('id');
  441. $expected = [
  442. 1 => ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  443. 3 => ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  444. 2 => ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  445. ];
  446. $this->assertEquals($expected, iterator_to_array($grouped));
  447. $this->assertInstanceOf('Cake\Collection\Collection', $grouped);
  448. $grouped = $collection->indexBy(function ($element) {
  449. return $element['id'];
  450. });
  451. $this->assertEquals($expected, iterator_to_array($grouped));
  452. }
  453. /**
  454. * Tests indexBy with a deep property
  455. *
  456. * @return void
  457. */
  458. public function testIndexByDeep()
  459. {
  460. $items = [
  461. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  462. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  463. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  464. ];
  465. $collection = new Collection($items);
  466. $grouped = $collection->indexBy('thing.parent_id');
  467. $expected = [
  468. 10 => ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  469. 11 => ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  470. ];
  471. $this->assertEquals($expected, iterator_to_array($grouped));
  472. }
  473. /**
  474. * Tests countBy
  475. *
  476. * @return void
  477. */
  478. public function testCountBy()
  479. {
  480. $items = [
  481. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  482. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  483. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  484. ];
  485. $collection = new Collection($items);
  486. $grouped = $collection->countBy('parent_id');
  487. $expected = [
  488. 10 => 2,
  489. 11 => 1
  490. ];
  491. $this->assertEquals($expected, iterator_to_array($grouped));
  492. $this->assertInstanceOf('Cake\Collection\Collection', $grouped);
  493. $grouped = $collection->countBy(function ($element) {
  494. return $element['parent_id'];
  495. });
  496. $this->assertEquals($expected, iterator_to_array($grouped));
  497. }
  498. /**
  499. * Tests shuffle
  500. *
  501. * @return void
  502. */
  503. public function testShuffle()
  504. {
  505. $data = [1, 2, 3, 4];
  506. $collection = (new Collection($data))->shuffle();
  507. $this->assertEquals(count($data), count(iterator_to_array($collection)));
  508. foreach ($collection as $value) {
  509. $this->assertContains($value, $data);
  510. }
  511. }
  512. /**
  513. * Tests sample
  514. *
  515. * @return void
  516. */
  517. public function testSample()
  518. {
  519. $data = [1, 2, 3, 4];
  520. $collection = (new Collection($data))->sample(2);
  521. $this->assertEquals(2, count(iterator_to_array($collection)));
  522. foreach ($collection as $value) {
  523. $this->assertContains($value, $data);
  524. }
  525. }
  526. /**
  527. * Test toArray method
  528. *
  529. * @return void
  530. */
  531. public function testToArray()
  532. {
  533. $data = [1, 2, 3, 4];
  534. $collection = new Collection($data);
  535. $this->assertEquals($data, $collection->toArray());
  536. }
  537. /**
  538. * Test toList method
  539. *
  540. * @return void
  541. */
  542. public function testToList()
  543. {
  544. $data = [100 => 1, 300 => 2, 500 => 3, 1 => 4];
  545. $collection = new Collection($data);
  546. $this->assertEquals(array_values($data), $collection->toList());
  547. }
  548. /**
  549. * Test json encoding
  550. *
  551. * @return void
  552. */
  553. public function testToJson()
  554. {
  555. $data = [1, 2, 3, 4];
  556. $collection = new Collection($data);
  557. $this->assertEquals(json_encode($data), json_encode($collection));
  558. }
  559. /**
  560. * Tests that only arrays and Traversables are allowed in the constructor
  561. *
  562. * @expectedException \InvalidArgumentException
  563. * @expectedExceptionMessage Only an array or \Traversable is allowed for Collection
  564. * @return void
  565. */
  566. public function testInvalidConstructorArgument()
  567. {
  568. new Collection('Derp');
  569. }
  570. /**
  571. * Tests take method
  572. *
  573. * @return void
  574. */
  575. public function testTake()
  576. {
  577. $data = [1, 2, 3, 4];
  578. $collection = new Collection($data);
  579. $taken = $collection->take(2);
  580. $this->assertEquals([1, 2], $taken->toArray());
  581. $taken = $collection->take(3);
  582. $this->assertEquals([1, 2, 3], $taken->toArray());
  583. $taken = $collection->take(500);
  584. $this->assertEquals([1, 2, 3, 4], $taken->toArray());
  585. $taken = $collection->take(1);
  586. $this->assertEquals([1], $taken->toArray());
  587. $taken = $collection->take();
  588. $this->assertEquals([1], $taken->toArray());
  589. $taken = $collection->take(2, 2);
  590. $this->assertEquals([2 => 3, 3 => 4], $taken->toArray());
  591. }
  592. /**
  593. * Tests match
  594. *
  595. * @return void
  596. */
  597. public function testMatch()
  598. {
  599. $items = [
  600. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  601. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  602. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  603. ];
  604. $collection = new Collection($items);
  605. $matched = $collection->match(['thing.parent_id' => 10, 'name' => 'baz']);
  606. $this->assertEquals([2 => $items[2]], $matched->toArray());
  607. $matched = $collection->match(['thing.parent_id' => 10]);
  608. $this->assertEquals(
  609. [0 => $items[0], 2 => $items[2]],
  610. $matched->toArray()
  611. );
  612. $matched = $collection->match(['thing.parent_id' => 500]);
  613. $this->assertEquals([], $matched->toArray());
  614. $matched = $collection->match(['parent_id' => 10, 'name' => 'baz']);
  615. $this->assertEquals([], $matched->toArray());
  616. }
  617. /**
  618. * Tests firstMatch
  619. *
  620. * @return void
  621. */
  622. public function testFirstMatch()
  623. {
  624. $items = [
  625. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  626. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  627. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  628. ];
  629. $collection = new Collection($items);
  630. $matched = $collection->firstMatch(['thing.parent_id' => 10]);
  631. $this->assertEquals(
  632. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  633. $matched
  634. );
  635. $matched = $collection->firstMatch(['thing.parent_id' => 10, 'name' => 'baz']);
  636. $this->assertEquals(
  637. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  638. $matched
  639. );
  640. }
  641. /**
  642. * Tests the append method
  643. *
  644. * @return void
  645. */
  646. public function testAppend()
  647. {
  648. $collection = new Collection([1, 2, 3]);
  649. $combined = $collection->append([4, 5, 6]);
  650. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toArray(false));
  651. $collection = new Collection(['a' => 1, 'b' => 2]);
  652. $combined = $collection->append(['c' => 3, 'a' => 4]);
  653. $this->assertEquals(['a' => 4, 'b' => 2, 'c' => 3], $combined->toArray());
  654. }
  655. /**
  656. * Tests the append method with iterator
  657. */
  658. public function testAppendIterator()
  659. {
  660. $collection = new Collection([1, 2, 3]);
  661. $iterator = new ArrayIterator([4, 5, 6]);
  662. $combined = $collection->append($iterator);
  663. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toList());
  664. }
  665. public function testAppendNotCollectionInstance()
  666. {
  667. $collection = new TestCollection([1, 2, 3]);
  668. $combined = $collection->append([4, 5, 6]);
  669. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toList());
  670. }
  671. /**
  672. * Tests that by calling compile internal iteration operations are not done
  673. * more than once
  674. *
  675. * @return void
  676. */
  677. public function testCompile()
  678. {
  679. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  680. $collection = new Collection($items);
  681. $callable = $this->getMock('stdClass', ['__invoke']);
  682. $callable->expects($this->at(0))
  683. ->method('__invoke')
  684. ->with(1, 'a')
  685. ->will($this->returnValue(4));
  686. $callable->expects($this->at(1))
  687. ->method('__invoke')
  688. ->with(2, 'b')
  689. ->will($this->returnValue(5));
  690. $callable->expects($this->at(2))
  691. ->method('__invoke')
  692. ->with(3, 'c')
  693. ->will($this->returnValue(6));
  694. $compiled = $collection->map($callable)->compile();
  695. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  696. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  697. }
  698. /**
  699. * Tests converting a non rewindable iterator into a rewindable one using
  700. * the buffered method.
  701. *
  702. * @return void
  703. */
  704. public function testBuffered()
  705. {
  706. $items = new NoRewindIterator(new ArrayIterator(['a' => 4, 'b' => 5, 'c' => 6]));
  707. $buffered = (new Collection($items))->buffered();
  708. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $buffered->toArray());
  709. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $buffered->toArray());
  710. }
  711. /**
  712. * Tests the combine method
  713. *
  714. * @return void
  715. */
  716. public function testCombine()
  717. {
  718. $items = [
  719. ['id' => 1, 'name' => 'foo', 'parent' => 'a'],
  720. ['id' => 2, 'name' => 'bar', 'parent' => 'b'],
  721. ['id' => 3, 'name' => 'baz', 'parent' => 'a']
  722. ];
  723. $collection = (new Collection($items))->combine('id', 'name');
  724. $expected = [1 => 'foo', 2 => 'bar', 3 => 'baz'];
  725. $this->assertEquals($expected, $collection->toArray());
  726. $expected = ['foo' => 1, 'bar' => 2, 'baz' => 3];
  727. $collection = (new Collection($items))->combine('name', 'id');
  728. $this->assertEquals($expected, $collection->toArray());
  729. $collection = (new Collection($items))->combine('id', 'name', 'parent');
  730. $expected = ['a' => [1 => 'foo', 3 => 'baz'], 'b' => [2 => 'bar']];
  731. $this->assertEquals($expected, $collection->toArray());
  732. $expected = [
  733. '0-1' => ['foo-0-1' => '0-1-foo'],
  734. '1-2' => ['bar-1-2' => '1-2-bar'],
  735. '2-3' => ['baz-2-3' => '2-3-baz']
  736. ];
  737. $collection = (new Collection($items))->combine(
  738. function ($value, $key) {
  739. return $value['name'] . '-' . $key;
  740. },
  741. function ($value, $key) {
  742. return $key . '-' . $value['name'];
  743. },
  744. function ($value, $key) {
  745. return $key . '-' . $value['id'];
  746. }
  747. );
  748. $this->assertEquals($expected, $collection->toArray());
  749. $collection = (new Collection($items))->combine('id', 'crazy');
  750. $this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
  751. }
  752. /**
  753. * Tests the nest method with only one level
  754. *
  755. * @return void
  756. */
  757. public function testNest()
  758. {
  759. $items = [
  760. ['id' => 1, 'parent_id' => null],
  761. ['id' => 2, 'parent_id' => 1],
  762. ['id' => 3, 'parent_id' => 1],
  763. ['id' => 4, 'parent_id' => 1],
  764. ['id' => 5, 'parent_id' => 6],
  765. ['id' => 6, 'parent_id' => null],
  766. ['id' => 7, 'parent_id' => 1],
  767. ['id' => 8, 'parent_id' => 6],
  768. ['id' => 9, 'parent_id' => 6],
  769. ['id' => 10, 'parent_id' => 6]
  770. ];
  771. $collection = (new Collection($items))->nest('id', 'parent_id');
  772. $expected = [
  773. [
  774. 'id' => 1,
  775. 'parent_id' => null,
  776. 'children' => [
  777. ['id' => 2, 'parent_id' => 1, 'children' => []],
  778. ['id' => 3, 'parent_id' => 1, 'children' => []],
  779. ['id' => 4, 'parent_id' => 1, 'children' => []],
  780. ['id' => 7, 'parent_id' => 1, 'children' => []]
  781. ]
  782. ],
  783. [
  784. 'id' => 6,
  785. 'parent_id' => null,
  786. 'children' => [
  787. ['id' => 5, 'parent_id' => 6, 'children' => []],
  788. ['id' => 8, 'parent_id' => 6, 'children' => []],
  789. ['id' => 9, 'parent_id' => 6, 'children' => []],
  790. ['id' => 10, 'parent_id' => 6, 'children' => []]
  791. ]
  792. ]
  793. ];
  794. $this->assertEquals($expected, $collection->toArray());
  795. }
  796. /**
  797. * Tests the nest method with more than one level
  798. *
  799. * @return void
  800. */
  801. public function testNestMultiLevel()
  802. {
  803. $items = [
  804. ['id' => 1, 'parent_id' => null],
  805. ['id' => 2, 'parent_id' => 1],
  806. ['id' => 3, 'parent_id' => 2],
  807. ['id' => 4, 'parent_id' => 2],
  808. ['id' => 5, 'parent_id' => 3],
  809. ['id' => 6, 'parent_id' => null],
  810. ['id' => 7, 'parent_id' => 3],
  811. ['id' => 8, 'parent_id' => 4],
  812. ['id' => 9, 'parent_id' => 6],
  813. ['id' => 10, 'parent_id' => 6]
  814. ];
  815. $collection = (new Collection($items))->nest('id', 'parent_id');
  816. $expected = [
  817. [
  818. 'id' => 1,
  819. 'parent_id' => null,
  820. 'children' => [
  821. [
  822. 'id' => 2,
  823. 'parent_id' => 1,
  824. 'children' => [
  825. [
  826. 'id' => 3,
  827. 'parent_id' => 2,
  828. 'children' => [
  829. ['id' => 5, 'parent_id' => 3, 'children' => []],
  830. ['id' => 7, 'parent_id' => 3, 'children' => []]
  831. ]
  832. ],
  833. [
  834. 'id' => 4,
  835. 'parent_id' => 2,
  836. 'children' => [
  837. ['id' => 8, 'parent_id' => 4, 'children' => []]
  838. ]
  839. ]
  840. ]
  841. ]
  842. ]
  843. ],
  844. [
  845. 'id' => 6,
  846. 'parent_id' => null,
  847. 'children' => [
  848. ['id' => 9, 'parent_id' => 6, 'children' => []],
  849. ['id' => 10, 'parent_id' => 6, 'children' => []]
  850. ]
  851. ]
  852. ];
  853. $this->assertEquals($expected, $collection->toArray());
  854. }
  855. /**
  856. * Tests the nest method with more than one level
  857. *
  858. * @return void
  859. */
  860. public function testNestObjects()
  861. {
  862. $items = [
  863. new ArrayObject(['id' => 1, 'parent_id' => null]),
  864. new ArrayObject(['id' => 2, 'parent_id' => 1]),
  865. new ArrayObject(['id' => 3, 'parent_id' => 2]),
  866. new ArrayObject(['id' => 4, 'parent_id' => 2]),
  867. new ArrayObject(['id' => 5, 'parent_id' => 3]),
  868. new ArrayObject(['id' => 6, 'parent_id' => null]),
  869. new ArrayObject(['id' => 7, 'parent_id' => 3]),
  870. new ArrayObject(['id' => 8, 'parent_id' => 4]),
  871. new ArrayObject(['id' => 9, 'parent_id' => 6]),
  872. new ArrayObject(['id' => 10, 'parent_id' => 6])
  873. ];
  874. $collection = (new Collection($items))->nest('id', 'parent_id');
  875. $expected = [
  876. new ArrayObject([
  877. 'id' => 1,
  878. 'parent_id' => null,
  879. 'children' => [
  880. new ArrayObject([
  881. 'id' => 2,
  882. 'parent_id' => 1,
  883. 'children' => [
  884. new ArrayObject([
  885. 'id' => 3,
  886. 'parent_id' => 2,
  887. 'children' => [
  888. new ArrayObject(['id' => 5, 'parent_id' => 3, 'children' => []]),
  889. new ArrayObject(['id' => 7, 'parent_id' => 3, 'children' => []])
  890. ]
  891. ]),
  892. new ArrayObject([
  893. 'id' => 4,
  894. 'parent_id' => 2,
  895. 'children' => [
  896. new ArrayObject(['id' => 8, 'parent_id' => 4, 'children' => []])
  897. ]
  898. ])
  899. ]
  900. ])
  901. ]
  902. ]),
  903. new ArrayObject([
  904. 'id' => 6,
  905. 'parent_id' => null,
  906. 'children' => [
  907. new ArrayObject(['id' => 9, 'parent_id' => 6, 'children' => []]),
  908. new ArrayObject(['id' => 10, 'parent_id' => 6, 'children' => []])
  909. ]
  910. ])
  911. ];
  912. $this->assertEquals($expected, $collection->toArray());
  913. }
  914. /**
  915. * Tests insert
  916. *
  917. * @return void
  918. */
  919. public function testInsert()
  920. {
  921. $items = [['a' => 1], ['b' => 2]];
  922. $collection = new Collection($items);
  923. $iterator = $collection->insert('c', [3, 4]);
  924. $this->assertInstanceOf('Cake\Collection\Iterator\InsertIterator', $iterator);
  925. $this->assertEquals(
  926. [['a' => 1, 'c' => 3], ['b' => 2, 'c' => 4]],
  927. iterator_to_array($iterator)
  928. );
  929. }
  930. /**
  931. * Provider for testing each of the directions for listNested
  932. *
  933. * @return void
  934. */
  935. public function nestedListProvider()
  936. {
  937. return [
  938. ['desc', [1, 2, 3, 5, 7, 4, 8, 6, 9, 10]],
  939. ['asc', [5, 7, 3, 8, 4, 2, 1, 9, 10, 6]],
  940. ['leaves', [5, 7, 8, 9, 10]]
  941. ];
  942. }
  943. /**
  944. * Tests the listNested method with the default 'children' nesting key
  945. *
  946. * @dataProvider nestedListProvider
  947. * @return void
  948. */
  949. public function testListNested($dir, $expected)
  950. {
  951. $items = [
  952. ['id' => 1, 'parent_id' => null],
  953. ['id' => 2, 'parent_id' => 1],
  954. ['id' => 3, 'parent_id' => 2],
  955. ['id' => 4, 'parent_id' => 2],
  956. ['id' => 5, 'parent_id' => 3],
  957. ['id' => 6, 'parent_id' => null],
  958. ['id' => 7, 'parent_id' => 3],
  959. ['id' => 8, 'parent_id' => 4],
  960. ['id' => 9, 'parent_id' => 6],
  961. ['id' => 10, 'parent_id' => 6]
  962. ];
  963. $collection = (new Collection($items))->nest('id', 'parent_id')->listNested($dir);
  964. $this->assertEquals($expected, $collection->extract('id')->toArray(false));
  965. }
  966. /**
  967. * Tests using listNested with a different nesting key
  968. *
  969. * @return void
  970. */
  971. public function testListNestedCustomKey()
  972. {
  973. $items = [
  974. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  975. ['id' => 4, 'stuff' => [['id' => 5]]]
  976. ];
  977. $collection = (new Collection($items))->listNested('desc', 'stuff');
  978. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  979. }
  980. /**
  981. * Tests flattening the collection using a custom callable function
  982. *
  983. * @return void
  984. */
  985. public function testListNestedWithCallable()
  986. {
  987. $items = [
  988. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  989. ['id' => 4, 'stuff' => [['id' => 5]]]
  990. ];
  991. $collection = (new Collection($items))->listNested('desc', function ($item) {
  992. return isset($item['stuff']) ? $item['stuff'] : [];
  993. });
  994. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  995. }
  996. /**
  997. * Tests the sumOf method
  998. *
  999. * @return void
  1000. */
  1001. public function testSumOf()
  1002. {
  1003. $items = [
  1004. ['invoice' => ['total' => 100]],
  1005. ['invoice' => ['total' => 200]]
  1006. ];
  1007. $this->assertEquals(300, (new Collection($items))->sumOf('invoice.total'));
  1008. $sum = (new Collection($items))->sumOf(function ($v) {
  1009. return $v['invoice']['total'] * 2;
  1010. });
  1011. $this->assertEquals(600, $sum);
  1012. }
  1013. /**
  1014. * Tests the stopWhen method with a callable
  1015. *
  1016. * @return void
  1017. */
  1018. public function testStopWhenCallable()
  1019. {
  1020. $items = [10, 20, 40, 10, 5];
  1021. $collection = (new Collection($items))->stopWhen(function ($v) {
  1022. return $v > 20;
  1023. });
  1024. $this->assertEquals([10, 20], $collection->toArray());
  1025. }
  1026. /**
  1027. * Tests the stopWhen method with a matching array
  1028. *
  1029. * @return void
  1030. */
  1031. public function testStopWhenWithArray()
  1032. {
  1033. $items = [
  1034. ['foo' => 'bar'],
  1035. ['foo' => 'baz'],
  1036. ['foo' => 'foo']
  1037. ];
  1038. $collection = (new Collection($items))->stopWhen(['foo' => 'baz']);
  1039. $this->assertEquals([['foo' => 'bar']], $collection->toArray());
  1040. }
  1041. /**
  1042. * Tests the unfold method
  1043. *
  1044. * @return void
  1045. */
  1046. public function testUnfold()
  1047. {
  1048. $items = [
  1049. [1, 2, 3, 4],
  1050. [5, 6],
  1051. [7, 8]
  1052. ];
  1053. $collection = (new Collection($items))->unfold();
  1054. $this->assertEquals(range(1, 8), $collection->toArray(false));
  1055. $items = [
  1056. [1, 2],
  1057. new Collection([3, 4])
  1058. ];
  1059. $collection = (new Collection($items))->unfold();
  1060. $this->assertEquals(range(1, 4), $collection->toArray(false));
  1061. }
  1062. /**
  1063. * Tests the unfold method with empty levels
  1064. *
  1065. * @return void
  1066. */
  1067. public function testUnfoldEmptyLevels()
  1068. {
  1069. $items = [[], [1, 2], []];
  1070. $collection = (new Collection($items))->unfold();
  1071. $this->assertEquals(range(1, 2), $collection->toArray(false));
  1072. $items = [];
  1073. $collection = (new Collection($items))->unfold();
  1074. $this->assertEmpty($collection->toArray(false));
  1075. }
  1076. /**
  1077. * Tests the unfold when passing a callable
  1078. *
  1079. * @return void
  1080. */
  1081. public function testUnfoldWithCallable()
  1082. {
  1083. $items = [1, 2, 3];
  1084. $collection = (new Collection($items))->unfold(function ($item) {
  1085. return range($item, $item * 2);
  1086. });
  1087. $expected = [1, 2, 2, 3, 4, 3, 4, 5, 6];
  1088. $this->assertEquals($expected, $collection->toArray(false));
  1089. }
  1090. /**
  1091. * Tests the through() method
  1092. *
  1093. * @return void
  1094. */
  1095. public function testThrough()
  1096. {
  1097. $items = [1, 2, 3];
  1098. $collection = (new Collection($items))->through(function ($collection) {
  1099. return $collection->append($collection->toList());
  1100. });
  1101. $this->assertEquals([1, 2, 3, 1, 2, 3], $collection->toList());
  1102. }
  1103. /**
  1104. * Tests the through method when it returns an array
  1105. *
  1106. * @return void
  1107. */
  1108. public function testThroughReturnArray()
  1109. {
  1110. $items = [1, 2, 3];
  1111. $collection = (new Collection($items))->through(function ($collection) {
  1112. $list = $collection->toList();
  1113. return array_merge($list, $list);
  1114. });
  1115. $this->assertEquals([1, 2, 3, 1, 2, 3], $collection->toList());
  1116. }
  1117. /**
  1118. * Tests that the sortBy method does not die when something that is not a
  1119. * collection is passed
  1120. *
  1121. * @return void
  1122. */
  1123. public function testComplexSortBy()
  1124. {
  1125. $results = collection([3, 7])
  1126. ->unfold(function ($value) {
  1127. return [
  1128. ['sorting' => $value * 2],
  1129. ['sorting' => $value * 2]
  1130. ];
  1131. })
  1132. ->sortBy('sorting')
  1133. ->extract('sorting')
  1134. ->toList();
  1135. $this->assertEquals([14, 14, 6, 6], $results);
  1136. }
  1137. /**
  1138. * Tests __debugInfo() or debug() usage
  1139. *
  1140. * @return void
  1141. */
  1142. public function testDebug()
  1143. {
  1144. $items = [1, 2, 3];
  1145. $collection = new Collection($items);
  1146. $result = $collection->__debugInfo();
  1147. $expected = [
  1148. 'count' => 3,
  1149. ];
  1150. $this->assertSame($expected, $result);
  1151. // Calling it again will rewind
  1152. $result = $collection->__debugInfo();
  1153. $expected = [
  1154. 'count' => 3,
  1155. ];
  1156. $this->assertSame($expected, $result);
  1157. // Make sure it also works with non rewindable iterators
  1158. $iterator = new NoRewindIterator(new ArrayIterator($items));
  1159. $collection = new Collection($iterator);
  1160. $result = $collection->__debugInfo();
  1161. $expected = [
  1162. 'count' => 3,
  1163. ];
  1164. $this->assertSame($expected, $result);
  1165. // Calling it again will in this case not rewind
  1166. $result = $collection->__debugInfo();
  1167. $expected = [
  1168. 'count' => 0,
  1169. ];
  1170. $this->assertSame($expected, $result);
  1171. }
  1172. /**
  1173. * Tests the isEmpty() method
  1174. *
  1175. * @return void
  1176. */
  1177. public function testIsEmpty()
  1178. {
  1179. $collection = new Collection([1, 2, 3]);
  1180. $this->assertFalse($collection->isEmpty());
  1181. $collection = $collection->map(function () {
  1182. return null;
  1183. });
  1184. $this->assertFalse($collection->isEmpty());
  1185. $collection = $collection->filter();
  1186. $this->assertTrue($collection->isEmpty());
  1187. }
  1188. /**
  1189. * Tests the zip() method
  1190. *
  1191. * @return void
  1192. */
  1193. public function testZip()
  1194. {
  1195. $collection = new Collection([1, 2]);
  1196. $zipped = $collection->zip([3, 4]);
  1197. $this->assertEquals([[1, 3], [2, 4]], $zipped->toList());
  1198. $collection = new Collection([1, 2]);
  1199. $zipped = $collection->zip([3]);
  1200. $this->assertEquals([[1, 3]], $zipped->toList());
  1201. $collection = new Collection([1, 2]);
  1202. $zipped = $collection->zip([3, 4], [5, 6], [7, 8], [9, 10, 11]);
  1203. $this->assertEquals([
  1204. [1, 3, 5, 7, 9],
  1205. [2, 4, 6, 8, 10]
  1206. ], $zipped->toList());
  1207. }
  1208. /**
  1209. * Tests the zipWith() method
  1210. *
  1211. * @return void
  1212. */
  1213. public function testZipWith()
  1214. {
  1215. $collection = new Collection([1, 2]);
  1216. $zipped = $collection->zipWith([3, 4], function ($a, $b) {
  1217. return $a * $b;
  1218. });
  1219. $this->assertEquals([3, 8], $zipped->toList());
  1220. $zipped = $collection->zipWith([3, 4], [5, 6, 7], function () {
  1221. return array_sum(func_get_args());
  1222. });
  1223. $this->assertEquals([9, 12], $zipped->toList());
  1224. }
  1225. /**
  1226. * Tests the skip() method
  1227. *
  1228. * @return void
  1229. */
  1230. public function testSkip()
  1231. {
  1232. $collection = new Collection([1, 2, 3, 4, 5]);
  1233. $this->assertEquals([3, 4, 5], $collection->skip(2)->toList());
  1234. $this->assertEquals([5], $collection->skip(4)->toList());
  1235. }
  1236. /**
  1237. * Tests the last() method
  1238. *
  1239. * @return void
  1240. */
  1241. public function testLast()
  1242. {
  1243. $collection = new Collection([1, 2, 3]);
  1244. $this->assertEquals(3, $collection->last());
  1245. $collection = $collection->map(function ($e) {
  1246. return $e * 2;
  1247. });
  1248. $this->assertEquals(6, $collection->last());
  1249. }
  1250. /**
  1251. * Tests the last() method when on an empty collection
  1252. *
  1253. * @return void
  1254. */
  1255. public function testLAstWithEmptyCollection()
  1256. {
  1257. $collection = new Collection([]);
  1258. $this->assertNull($collection->last());
  1259. }
  1260. /**
  1261. * Tests sumOf with no parameters
  1262. *
  1263. * @return void
  1264. */
  1265. public function testSumOfWithIdentity()
  1266. {
  1267. $collection = new Collection([1, 2, 3]);
  1268. $this->assertEquals(6, $collection->sumOf());
  1269. $collection = new Collection(['a' => 1, 'b' => 4, 'c' => 6]);
  1270. $this->assertEquals(11, $collection->sumOf());
  1271. }
  1272. public function testUnfoldedExtract()
  1273. {
  1274. $items = [
  1275. ['comments' => [['id' => 1], ['id' => 2]]],
  1276. ['comments' => [['id' => 3], ['id' => 4]]],
  1277. ['comments' => [['id' => 7], ['nope' => 8]]],
  1278. ];
  1279. $extracted = (new Collection($items))->extract('comments.{n}.id');
  1280. $this->assertEquals([1, 2, 3, 4, 7, null], $extracted->toList());
  1281. $items = [
  1282. [
  1283. 'comments' => [
  1284. [
  1285. 'voters' => [['id' => 1], ['id' => 2]]
  1286. ]
  1287. ]
  1288. ],
  1289. [
  1290. 'comments' => [
  1291. [
  1292. 'voters' => [['id' => 3], ['id' => 4]]
  1293. ]
  1294. ]
  1295. ],
  1296. [
  1297. 'comments' => [
  1298. [
  1299. 'voters' => [['id' => 5], ['nope' => 'fail'], ['id' => 6]]
  1300. ]
  1301. ]
  1302. ],
  1303. [
  1304. 'comments' => [
  1305. [
  1306. 'not_voters' => [['id' => 5]]
  1307. ]
  1308. ]
  1309. ],
  1310. ['not_comments' => []]
  1311. ];
  1312. $extracted = (new Collection($items))->extract('comments.{n}.voters.{n}.id');
  1313. $expected = [1, 2, 3, 4, 5, null, 6];
  1314. $this->assertEquals($expected, $extracted->toList());
  1315. }
  1316. }