CollectionTest.php 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
  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 that issuing a count will throw an exception
  572. *
  573. * @expectedException \LogicException
  574. * @return void
  575. */
  576. public function testCollectionCount()
  577. {
  578. $data = [1, 2, 3, 4];
  579. $collection = new Collection($data);
  580. $collection->count();
  581. }
  582. /**
  583. * Tests take method
  584. *
  585. * @return void
  586. */
  587. public function testTake()
  588. {
  589. $data = [1, 2, 3, 4];
  590. $collection = new Collection($data);
  591. $taken = $collection->take(2);
  592. $this->assertEquals([1, 2], $taken->toArray());
  593. $taken = $collection->take(3);
  594. $this->assertEquals([1, 2, 3], $taken->toArray());
  595. $taken = $collection->take(500);
  596. $this->assertEquals([1, 2, 3, 4], $taken->toArray());
  597. $taken = $collection->take(1);
  598. $this->assertEquals([1], $taken->toArray());
  599. $taken = $collection->take();
  600. $this->assertEquals([1], $taken->toArray());
  601. $taken = $collection->take(2, 2);
  602. $this->assertEquals([2 => 3, 3 => 4], $taken->toArray());
  603. }
  604. /**
  605. * Tests match
  606. *
  607. * @return void
  608. */
  609. public function testMatch()
  610. {
  611. $items = [
  612. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  613. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  614. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  615. ];
  616. $collection = new Collection($items);
  617. $matched = $collection->match(['thing.parent_id' => 10, 'name' => 'baz']);
  618. $this->assertEquals([2 => $items[2]], $matched->toArray());
  619. $matched = $collection->match(['thing.parent_id' => 10]);
  620. $this->assertEquals(
  621. [0 => $items[0], 2 => $items[2]],
  622. $matched->toArray()
  623. );
  624. $matched = $collection->match(['thing.parent_id' => 500]);
  625. $this->assertEquals([], $matched->toArray());
  626. $matched = $collection->match(['parent_id' => 10, 'name' => 'baz']);
  627. $this->assertEquals([], $matched->toArray());
  628. }
  629. /**
  630. * Tests firstMatch
  631. *
  632. * @return void
  633. */
  634. public function testFirstMatch()
  635. {
  636. $items = [
  637. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  638. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  639. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  640. ];
  641. $collection = new Collection($items);
  642. $matched = $collection->firstMatch(['thing.parent_id' => 10]);
  643. $this->assertEquals(
  644. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  645. $matched
  646. );
  647. $matched = $collection->firstMatch(['thing.parent_id' => 10, 'name' => 'baz']);
  648. $this->assertEquals(
  649. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  650. $matched
  651. );
  652. }
  653. /**
  654. * Tests the append method
  655. *
  656. * @return void
  657. */
  658. public function testAppend()
  659. {
  660. $collection = new Collection([1, 2, 3]);
  661. $combined = $collection->append([4, 5, 6]);
  662. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toArray(false));
  663. $collection = new Collection(['a' => 1, 'b' => 2]);
  664. $combined = $collection->append(['c' => 3, 'a' => 4]);
  665. $this->assertEquals(['a' => 4, 'b' => 2, 'c' => 3], $combined->toArray());
  666. }
  667. /**
  668. * Tests the append method with iterator
  669. */
  670. public function testAppendIterator()
  671. {
  672. $collection = new Collection([1, 2, 3]);
  673. $iterator = new ArrayIterator([4, 5, 6]);
  674. $combined = $collection->append($iterator);
  675. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toList());
  676. }
  677. public function testAppendNotCollectionInstance()
  678. {
  679. $collection = new TestCollection([1, 2, 3]);
  680. $combined = $collection->append([4, 5, 6]);
  681. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toList());
  682. }
  683. /**
  684. * Tests that by calling compile internal iteration operations are not done
  685. * more than once
  686. *
  687. * @return void
  688. */
  689. public function testCompile()
  690. {
  691. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  692. $collection = new Collection($items);
  693. $callable = $this->getMock('stdClass', ['__invoke']);
  694. $callable->expects($this->at(0))
  695. ->method('__invoke')
  696. ->with(1, 'a')
  697. ->will($this->returnValue(4));
  698. $callable->expects($this->at(1))
  699. ->method('__invoke')
  700. ->with(2, 'b')
  701. ->will($this->returnValue(5));
  702. $callable->expects($this->at(2))
  703. ->method('__invoke')
  704. ->with(3, 'c')
  705. ->will($this->returnValue(6));
  706. $compiled = $collection->map($callable)->compile();
  707. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  708. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  709. }
  710. /**
  711. * Tests converting a non rewindable iterator into a rewindable one using
  712. * the buffered method.
  713. *
  714. * @return void
  715. */
  716. public function testBuffered()
  717. {
  718. $items = new NoRewindIterator(new ArrayIterator(['a' => 4, 'b' => 5, 'c' => 6]));
  719. $buffered = (new Collection($items))->buffered();
  720. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $buffered->toArray());
  721. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $buffered->toArray());
  722. }
  723. /**
  724. * Tests the combine method
  725. *
  726. * @return void
  727. */
  728. public function testCombine()
  729. {
  730. $items = [
  731. ['id' => 1, 'name' => 'foo', 'parent' => 'a'],
  732. ['id' => 2, 'name' => 'bar', 'parent' => 'b'],
  733. ['id' => 3, 'name' => 'baz', 'parent' => 'a']
  734. ];
  735. $collection = (new Collection($items))->combine('id', 'name');
  736. $expected = [1 => 'foo', 2 => 'bar', 3 => 'baz'];
  737. $this->assertEquals($expected, $collection->toArray());
  738. $expected = ['foo' => 1, 'bar' => 2, 'baz' => 3];
  739. $collection = (new Collection($items))->combine('name', 'id');
  740. $this->assertEquals($expected, $collection->toArray());
  741. $collection = (new Collection($items))->combine('id', 'name', 'parent');
  742. $expected = ['a' => [1 => 'foo', 3 => 'baz'], 'b' => [2 => 'bar']];
  743. $this->assertEquals($expected, $collection->toArray());
  744. $expected = [
  745. '0-1' => ['foo-0-1' => '0-1-foo'],
  746. '1-2' => ['bar-1-2' => '1-2-bar'],
  747. '2-3' => ['baz-2-3' => '2-3-baz']
  748. ];
  749. $collection = (new Collection($items))->combine(
  750. function ($value, $key) {
  751. return $value['name'] . '-' . $key;
  752. },
  753. function ($value, $key) {
  754. return $key . '-' . $value['name'];
  755. },
  756. function ($value, $key) {
  757. return $key . '-' . $value['id'];
  758. }
  759. );
  760. $this->assertEquals($expected, $collection->toArray());
  761. $collection = (new Collection($items))->combine('id', 'crazy');
  762. $this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
  763. }
  764. /**
  765. * Tests the nest method with only one level
  766. *
  767. * @return void
  768. */
  769. public function testNest()
  770. {
  771. $items = [
  772. ['id' => 1, 'parent_id' => null],
  773. ['id' => 2, 'parent_id' => 1],
  774. ['id' => 3, 'parent_id' => 1],
  775. ['id' => 4, 'parent_id' => 1],
  776. ['id' => 5, 'parent_id' => 6],
  777. ['id' => 6, 'parent_id' => null],
  778. ['id' => 7, 'parent_id' => 1],
  779. ['id' => 8, 'parent_id' => 6],
  780. ['id' => 9, 'parent_id' => 6],
  781. ['id' => 10, 'parent_id' => 6]
  782. ];
  783. $collection = (new Collection($items))->nest('id', 'parent_id');
  784. $expected = [
  785. [
  786. 'id' => 1,
  787. 'parent_id' => null,
  788. 'children' => [
  789. ['id' => 2, 'parent_id' => 1, 'children' => []],
  790. ['id' => 3, 'parent_id' => 1, 'children' => []],
  791. ['id' => 4, 'parent_id' => 1, 'children' => []],
  792. ['id' => 7, 'parent_id' => 1, 'children' => []]
  793. ]
  794. ],
  795. [
  796. 'id' => 6,
  797. 'parent_id' => null,
  798. 'children' => [
  799. ['id' => 5, 'parent_id' => 6, 'children' => []],
  800. ['id' => 8, 'parent_id' => 6, 'children' => []],
  801. ['id' => 9, 'parent_id' => 6, 'children' => []],
  802. ['id' => 10, 'parent_id' => 6, 'children' => []]
  803. ]
  804. ]
  805. ];
  806. $this->assertEquals($expected, $collection->toArray());
  807. }
  808. /**
  809. * Tests the nest method with more than one level
  810. *
  811. * @return void
  812. */
  813. public function testNestMultiLevel()
  814. {
  815. $items = [
  816. ['id' => 1, 'parent_id' => null],
  817. ['id' => 2, 'parent_id' => 1],
  818. ['id' => 3, 'parent_id' => 2],
  819. ['id' => 4, 'parent_id' => 2],
  820. ['id' => 5, 'parent_id' => 3],
  821. ['id' => 6, 'parent_id' => null],
  822. ['id' => 7, 'parent_id' => 3],
  823. ['id' => 8, 'parent_id' => 4],
  824. ['id' => 9, 'parent_id' => 6],
  825. ['id' => 10, 'parent_id' => 6]
  826. ];
  827. $collection = (new Collection($items))->nest('id', 'parent_id');
  828. $expected = [
  829. [
  830. 'id' => 1,
  831. 'parent_id' => null,
  832. 'children' => [
  833. [
  834. 'id' => 2,
  835. 'parent_id' => 1,
  836. 'children' => [
  837. [
  838. 'id' => 3,
  839. 'parent_id' => 2,
  840. 'children' => [
  841. ['id' => 5, 'parent_id' => 3, 'children' => []],
  842. ['id' => 7, 'parent_id' => 3, 'children' => []]
  843. ]
  844. ],
  845. [
  846. 'id' => 4,
  847. 'parent_id' => 2,
  848. 'children' => [
  849. ['id' => 8, 'parent_id' => 4, 'children' => []]
  850. ]
  851. ]
  852. ]
  853. ]
  854. ]
  855. ],
  856. [
  857. 'id' => 6,
  858. 'parent_id' => null,
  859. 'children' => [
  860. ['id' => 9, 'parent_id' => 6, 'children' => []],
  861. ['id' => 10, 'parent_id' => 6, 'children' => []]
  862. ]
  863. ]
  864. ];
  865. $this->assertEquals($expected, $collection->toArray());
  866. }
  867. /**
  868. * Tests the nest method with more than one level
  869. *
  870. * @return void
  871. */
  872. public function testNestObjects()
  873. {
  874. $items = [
  875. new ArrayObject(['id' => 1, 'parent_id' => null]),
  876. new ArrayObject(['id' => 2, 'parent_id' => 1]),
  877. new ArrayObject(['id' => 3, 'parent_id' => 2]),
  878. new ArrayObject(['id' => 4, 'parent_id' => 2]),
  879. new ArrayObject(['id' => 5, 'parent_id' => 3]),
  880. new ArrayObject(['id' => 6, 'parent_id' => null]),
  881. new ArrayObject(['id' => 7, 'parent_id' => 3]),
  882. new ArrayObject(['id' => 8, 'parent_id' => 4]),
  883. new ArrayObject(['id' => 9, 'parent_id' => 6]),
  884. new ArrayObject(['id' => 10, 'parent_id' => 6])
  885. ];
  886. $collection = (new Collection($items))->nest('id', 'parent_id');
  887. $expected = [
  888. new ArrayObject([
  889. 'id' => 1,
  890. 'parent_id' => null,
  891. 'children' => [
  892. new ArrayObject([
  893. 'id' => 2,
  894. 'parent_id' => 1,
  895. 'children' => [
  896. new ArrayObject([
  897. 'id' => 3,
  898. 'parent_id' => 2,
  899. 'children' => [
  900. new ArrayObject(['id' => 5, 'parent_id' => 3, 'children' => []]),
  901. new ArrayObject(['id' => 7, 'parent_id' => 3, 'children' => []])
  902. ]
  903. ]),
  904. new ArrayObject([
  905. 'id' => 4,
  906. 'parent_id' => 2,
  907. 'children' => [
  908. new ArrayObject(['id' => 8, 'parent_id' => 4, 'children' => []])
  909. ]
  910. ])
  911. ]
  912. ])
  913. ]
  914. ]),
  915. new ArrayObject([
  916. 'id' => 6,
  917. 'parent_id' => null,
  918. 'children' => [
  919. new ArrayObject(['id' => 9, 'parent_id' => 6, 'children' => []]),
  920. new ArrayObject(['id' => 10, 'parent_id' => 6, 'children' => []])
  921. ]
  922. ])
  923. ];
  924. $this->assertEquals($expected, $collection->toArray());
  925. }
  926. /**
  927. * Tests insert
  928. *
  929. * @return void
  930. */
  931. public function testInsert()
  932. {
  933. $items = [['a' => 1], ['b' => 2]];
  934. $collection = new Collection($items);
  935. $iterator = $collection->insert('c', [3, 4]);
  936. $this->assertInstanceOf('Cake\Collection\Iterator\InsertIterator', $iterator);
  937. $this->assertEquals(
  938. [['a' => 1, 'c' => 3], ['b' => 2, 'c' => 4]],
  939. iterator_to_array($iterator)
  940. );
  941. }
  942. /**
  943. * Provider for testing each of the directions for listNested
  944. *
  945. * @return void
  946. */
  947. public function nestedListProvider()
  948. {
  949. return [
  950. ['desc', [1, 2, 3, 5, 7, 4, 8, 6, 9, 10]],
  951. ['asc', [5, 7, 3, 8, 4, 2, 1, 9, 10, 6]],
  952. ['leaves', [5, 7, 8, 9, 10]]
  953. ];
  954. }
  955. /**
  956. * Tests the listNested method with the default 'children' nesting key
  957. *
  958. * @dataProvider nestedListProvider
  959. * @return void
  960. */
  961. public function testListNested($dir, $expected)
  962. {
  963. $items = [
  964. ['id' => 1, 'parent_id' => null],
  965. ['id' => 2, 'parent_id' => 1],
  966. ['id' => 3, 'parent_id' => 2],
  967. ['id' => 4, 'parent_id' => 2],
  968. ['id' => 5, 'parent_id' => 3],
  969. ['id' => 6, 'parent_id' => null],
  970. ['id' => 7, 'parent_id' => 3],
  971. ['id' => 8, 'parent_id' => 4],
  972. ['id' => 9, 'parent_id' => 6],
  973. ['id' => 10, 'parent_id' => 6]
  974. ];
  975. $collection = (new Collection($items))->nest('id', 'parent_id')->listNested($dir);
  976. $this->assertEquals($expected, $collection->extract('id')->toArray(false));
  977. }
  978. /**
  979. * Tests using listNested with a different nesting key
  980. *
  981. * @return void
  982. */
  983. public function testListNestedCustomKey()
  984. {
  985. $items = [
  986. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  987. ['id' => 4, 'stuff' => [['id' => 5]]]
  988. ];
  989. $collection = (new Collection($items))->listNested('desc', 'stuff');
  990. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  991. }
  992. /**
  993. * Tests flattening the collection using a custom callable function
  994. *
  995. * @return void
  996. */
  997. public function testListNestedWithCallable()
  998. {
  999. $items = [
  1000. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  1001. ['id' => 4, 'stuff' => [['id' => 5]]]
  1002. ];
  1003. $collection = (new Collection($items))->listNested('desc', function ($item) {
  1004. return isset($item['stuff']) ? $item['stuff'] : [];
  1005. });
  1006. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  1007. }
  1008. /**
  1009. * Tests the sumOf method
  1010. *
  1011. * @return void
  1012. */
  1013. public function testSumOf()
  1014. {
  1015. $items = [
  1016. ['invoice' => ['total' => 100]],
  1017. ['invoice' => ['total' => 200]]
  1018. ];
  1019. $this->assertEquals(300, (new Collection($items))->sumOf('invoice.total'));
  1020. $sum = (new Collection($items))->sumOf(function ($v) {
  1021. return $v['invoice']['total'] * 2;
  1022. });
  1023. $this->assertEquals(600, $sum);
  1024. }
  1025. /**
  1026. * Tests the stopWhen method with a callable
  1027. *
  1028. * @return void
  1029. */
  1030. public function testStopWhenCallable()
  1031. {
  1032. $items = [10, 20, 40, 10, 5];
  1033. $collection = (new Collection($items))->stopWhen(function ($v) {
  1034. return $v > 20;
  1035. });
  1036. $this->assertEquals([10, 20], $collection->toArray());
  1037. }
  1038. /**
  1039. * Tests the stopWhen method with a matching array
  1040. *
  1041. * @return void
  1042. */
  1043. public function testStopWhenWithArray()
  1044. {
  1045. $items = [
  1046. ['foo' => 'bar'],
  1047. ['foo' => 'baz'],
  1048. ['foo' => 'foo']
  1049. ];
  1050. $collection = (new Collection($items))->stopWhen(['foo' => 'baz']);
  1051. $this->assertEquals([['foo' => 'bar']], $collection->toArray());
  1052. }
  1053. /**
  1054. * Tests the unfold method
  1055. *
  1056. * @return void
  1057. */
  1058. public function testUnfold()
  1059. {
  1060. $items = [
  1061. [1, 2, 3, 4],
  1062. [5, 6],
  1063. [7, 8]
  1064. ];
  1065. $collection = (new Collection($items))->unfold();
  1066. $this->assertEquals(range(1, 8), $collection->toArray(false));
  1067. $items = [
  1068. [1, 2],
  1069. new Collection([3, 4])
  1070. ];
  1071. $collection = (new Collection($items))->unfold();
  1072. $this->assertEquals(range(1, 4), $collection->toArray(false));
  1073. }
  1074. /**
  1075. * Tests the unfold method with empty levels
  1076. *
  1077. * @return void
  1078. */
  1079. public function testUnfoldEmptyLevels()
  1080. {
  1081. $items = [[], [1, 2], []];
  1082. $collection = (new Collection($items))->unfold();
  1083. $this->assertEquals(range(1, 2), $collection->toArray(false));
  1084. $items = [];
  1085. $collection = (new Collection($items))->unfold();
  1086. $this->assertEmpty($collection->toArray(false));
  1087. }
  1088. /**
  1089. * Tests the unfold when passing a callable
  1090. *
  1091. * @return void
  1092. */
  1093. public function testUnfoldWithCallable()
  1094. {
  1095. $items = [1, 2, 3];
  1096. $collection = (new Collection($items))->unfold(function ($item) {
  1097. return range($item, $item * 2);
  1098. });
  1099. $expected = [1, 2, 2, 3, 4, 3, 4, 5, 6];
  1100. $this->assertEquals($expected, $collection->toArray(false));
  1101. }
  1102. /**
  1103. * Tests the through() method
  1104. *
  1105. * @return void
  1106. */
  1107. public function testThrough()
  1108. {
  1109. $items = [1, 2, 3];
  1110. $collection = (new Collection($items))->through(function ($collection) {
  1111. return $collection->append($collection->toList());
  1112. });
  1113. $this->assertEquals([1, 2, 3, 1, 2, 3], $collection->toList());
  1114. }
  1115. /**
  1116. * Tests the through method when it returns an array
  1117. *
  1118. * @return void
  1119. */
  1120. public function testThroughReturnArray()
  1121. {
  1122. $items = [1, 2, 3];
  1123. $collection = (new Collection($items))->through(function ($collection) {
  1124. $list = $collection->toList();
  1125. return array_merge($list, $list);
  1126. });
  1127. $this->assertEquals([1, 2, 3, 1, 2, 3], $collection->toList());
  1128. }
  1129. /**
  1130. * Tests that the sortBy method does not die when something that is not a
  1131. * collection is passed
  1132. *
  1133. * @return void
  1134. */
  1135. public function testComplexSortBy()
  1136. {
  1137. $results = collection([3, 7])
  1138. ->unfold(function ($value) {
  1139. return [
  1140. ['sorting' => $value * 2],
  1141. ['sorting' => $value * 2]
  1142. ];
  1143. })
  1144. ->sortBy('sorting')
  1145. ->extract('sorting')
  1146. ->toList();
  1147. $this->assertEquals([14, 14, 6, 6], $results);
  1148. }
  1149. /**
  1150. * Tests __debugInfo() or debug() usage
  1151. *
  1152. * @return void
  1153. */
  1154. public function testDebug()
  1155. {
  1156. $items = [1, 2, 3];
  1157. $collection = new Collection($items);
  1158. $result = $collection->__debugInfo();
  1159. $expected = [
  1160. 'count' => 3,
  1161. ];
  1162. $this->assertSame($expected, $result);
  1163. // Calling it again will rewind
  1164. $result = $collection->__debugInfo();
  1165. $expected = [
  1166. 'count' => 3,
  1167. ];
  1168. $this->assertSame($expected, $result);
  1169. // Make sure it also works with non rewindable iterators
  1170. $iterator = new NoRewindIterator(new ArrayIterator($items));
  1171. $collection = new Collection($iterator);
  1172. $result = $collection->__debugInfo();
  1173. $expected = [
  1174. 'count' => 3,
  1175. ];
  1176. $this->assertSame($expected, $result);
  1177. // Calling it again will in this case not rewind
  1178. $result = $collection->__debugInfo();
  1179. $expected = [
  1180. 'count' => 0,
  1181. ];
  1182. $this->assertSame($expected, $result);
  1183. }
  1184. /**
  1185. * Tests the isEmpty() method
  1186. *
  1187. * @return void
  1188. */
  1189. public function testIsEmpty()
  1190. {
  1191. $collection = new Collection([1, 2, 3]);
  1192. $this->assertFalse($collection->isEmpty());
  1193. $collection = $collection->map(function () {
  1194. return null;
  1195. });
  1196. $this->assertFalse($collection->isEmpty());
  1197. $collection = $collection->filter();
  1198. $this->assertTrue($collection->isEmpty());
  1199. }
  1200. /**
  1201. * Tests the isEmpty() method does not consume data
  1202. * from buffered iterators.
  1203. *
  1204. * @return void
  1205. */
  1206. public function testIsEmptyDoesNotConsume()
  1207. {
  1208. $array = new \ArrayIterator([1, 2, 3]);
  1209. $inner = new \Cake\Collection\Iterator\BufferedIterator($array);
  1210. $collection = new Collection($inner);
  1211. $this->assertFalse($collection->isEmpty());
  1212. $this->assertCount(3, $collection->toArray());
  1213. }
  1214. /**
  1215. * Tests the zip() method
  1216. *
  1217. * @return void
  1218. */
  1219. public function testZip()
  1220. {
  1221. $collection = new Collection([1, 2]);
  1222. $zipped = $collection->zip([3, 4]);
  1223. $this->assertEquals([[1, 3], [2, 4]], $zipped->toList());
  1224. $collection = new Collection([1, 2]);
  1225. $zipped = $collection->zip([3]);
  1226. $this->assertEquals([[1, 3]], $zipped->toList());
  1227. $collection = new Collection([1, 2]);
  1228. $zipped = $collection->zip([3, 4], [5, 6], [7, 8], [9, 10, 11]);
  1229. $this->assertEquals([
  1230. [1, 3, 5, 7, 9],
  1231. [2, 4, 6, 8, 10]
  1232. ], $zipped->toList());
  1233. }
  1234. /**
  1235. * Tests the zipWith() method
  1236. *
  1237. * @return void
  1238. */
  1239. public function testZipWith()
  1240. {
  1241. $collection = new Collection([1, 2]);
  1242. $zipped = $collection->zipWith([3, 4], function ($a, $b) {
  1243. return $a * $b;
  1244. });
  1245. $this->assertEquals([3, 8], $zipped->toList());
  1246. $zipped = $collection->zipWith([3, 4], [5, 6, 7], function () {
  1247. return array_sum(func_get_args());
  1248. });
  1249. $this->assertEquals([9, 12], $zipped->toList());
  1250. }
  1251. /**
  1252. * Tests the skip() method
  1253. *
  1254. * @return void
  1255. */
  1256. public function testSkip()
  1257. {
  1258. $collection = new Collection([1, 2, 3, 4, 5]);
  1259. $this->assertEquals([3, 4, 5], $collection->skip(2)->toList());
  1260. $this->assertEquals([5], $collection->skip(4)->toList());
  1261. }
  1262. /**
  1263. * Tests the last() method
  1264. *
  1265. * @return void
  1266. */
  1267. public function testLast()
  1268. {
  1269. $collection = new Collection([1, 2, 3]);
  1270. $this->assertEquals(3, $collection->last());
  1271. $collection = $collection->map(function ($e) {
  1272. return $e * 2;
  1273. });
  1274. $this->assertEquals(6, $collection->last());
  1275. }
  1276. /**
  1277. * Tests the last() method when on an empty collection
  1278. *
  1279. * @return void
  1280. */
  1281. public function testLAstWithEmptyCollection()
  1282. {
  1283. $collection = new Collection([]);
  1284. $this->assertNull($collection->last());
  1285. }
  1286. /**
  1287. * Tests sumOf with no parameters
  1288. *
  1289. * @return void
  1290. */
  1291. public function testSumOfWithIdentity()
  1292. {
  1293. $collection = new Collection([1, 2, 3]);
  1294. $this->assertEquals(6, $collection->sumOf());
  1295. $collection = new Collection(['a' => 1, 'b' => 4, 'c' => 6]);
  1296. $this->assertEquals(11, $collection->sumOf());
  1297. }
  1298. /**
  1299. * Tests using extract with the {*} notation
  1300. *
  1301. * @return void
  1302. */
  1303. public function testUnfoldedExtract()
  1304. {
  1305. $items = [
  1306. ['comments' => [['id' => 1], ['id' => 2]]],
  1307. ['comments' => [['id' => 3], ['id' => 4]]],
  1308. ['comments' => [['id' => 7], ['nope' => 8]]],
  1309. ];
  1310. $extracted = (new Collection($items))->extract('comments.{*}.id');
  1311. $this->assertEquals([1, 2, 3, 4, 7, null], $extracted->toArray());
  1312. $items = [
  1313. [
  1314. 'comments' => [
  1315. [
  1316. 'voters' => [['id' => 1], ['id' => 2]]
  1317. ]
  1318. ]
  1319. ],
  1320. [
  1321. 'comments' => [
  1322. [
  1323. 'voters' => [['id' => 3], ['id' => 4]]
  1324. ]
  1325. ]
  1326. ],
  1327. [
  1328. 'comments' => [
  1329. [
  1330. 'voters' => [['id' => 5], ['nope' => 'fail'], ['id' => 6]]
  1331. ]
  1332. ]
  1333. ],
  1334. [
  1335. 'comments' => [
  1336. [
  1337. 'not_voters' => [['id' => 5]]
  1338. ]
  1339. ]
  1340. ],
  1341. ['not_comments' => []]
  1342. ];
  1343. $extracted = (new Collection($items))->extract('comments.{*}.voters.{*}.id');
  1344. $expected = [1, 2, 3, 4, 5, null, 6];
  1345. $this->assertEquals($expected, $extracted->toArray());
  1346. $this->assertEquals($expected, $extracted->toList());
  1347. }
  1348. /**
  1349. * Tests serializing a simple collection
  1350. *
  1351. * @return void
  1352. */
  1353. public function testSerializeSimpleCollection()
  1354. {
  1355. $collection = new Collection([1, 2, 3]);
  1356. $selialized = serialize($collection);
  1357. $unserialized = unserialize($selialized);
  1358. $this->assertEquals($collection->toList(), $unserialized->toList());
  1359. $this->assertEquals($collection->toArray(), $unserialized->toArray());
  1360. }
  1361. /**
  1362. * Tests serialization when using append
  1363. *
  1364. * @return void
  1365. */
  1366. public function testSerializeWithAppendIterators()
  1367. {
  1368. $collection = new Collection([1, 2, 3]);
  1369. $collection = $collection->append(['a' => 4, 'b' => 5, 'c' => 6]);
  1370. $selialized = serialize($collection);
  1371. $unserialized = unserialize($selialized);
  1372. $this->assertEquals($collection->toList(), $unserialized->toList());
  1373. $this->assertEquals($collection->toArray(), $unserialized->toArray());
  1374. }
  1375. /**
  1376. * Tests serialization when using nested iterators
  1377. *
  1378. * @return void
  1379. */
  1380. public function testSerializeWithNestedIterators()
  1381. {
  1382. $collection = new Collection([1, 2, 3]);
  1383. $collection = $collection->map(function ($e) {
  1384. return $e * 3;
  1385. });
  1386. $collection = $collection->groupBy(function ($e) {
  1387. return $e % 2;
  1388. });
  1389. $selialized = serialize($collection);
  1390. $unserialized = unserialize($selialized);
  1391. $this->assertEquals($collection->toList(), $unserialized->toList());
  1392. $this->assertEquals($collection->toArray(), $unserialized->toArray());
  1393. }
  1394. /**
  1395. * Tests serializing a zip() call
  1396. *
  1397. * @return void
  1398. */
  1399. public function testSerializeWithZipIterator()
  1400. {
  1401. $collection = new Collection([4, 5]);
  1402. $collection = $collection->zip([1, 2]);
  1403. $selialized = serialize($collection);
  1404. $unserialized = unserialize($selialized);
  1405. $this->assertEquals($collection->toList(), $unserialized->toList());
  1406. }
  1407. /**
  1408. * Tests the chunk method with exact chunks
  1409. *
  1410. * @return void
  1411. */
  1412. public function testChunk()
  1413. {
  1414. $collection = new Collection(range(1, 10));
  1415. $chunked = $collection->chunk(2)->toList();
  1416. $expected = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]];
  1417. $this->assertEquals($expected, $chunked);
  1418. }
  1419. /**
  1420. * Tests the chunk method with overflowing chunk size
  1421. *
  1422. * @return void
  1423. */
  1424. public function testChunkOverflow()
  1425. {
  1426. $collection = new Collection(range(1, 11));
  1427. $chunked = $collection->chunk(2)->toList();
  1428. $expected = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]];
  1429. $this->assertEquals($expected, $chunked);
  1430. }
  1431. /**
  1432. * Tests the chunk method with non-scalar items
  1433. *
  1434. * @return void
  1435. */
  1436. public function testChunkNested()
  1437. {
  1438. $collection = new Collection([1, 2, 3, [4, 5], 6, [7, [8, 9], 10], 11]);
  1439. $chunked = $collection->chunk(2)->toList();
  1440. $expected = [[1, 2], [3, [4, 5]], [6, [7, [8, 9], 10]], [11]];
  1441. $this->assertEquals($expected, $chunked);
  1442. }
  1443. }