Navigation Menu

Skip to content

Commit

Permalink
Refactoring and documenting functions
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Dec 18, 2014
1 parent 39607af commit 5fe3e34
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 20 deletions.
35 changes: 35 additions & 0 deletions src/Collection/CollectionInterface.php
Expand Up @@ -765,4 +765,39 @@ public function listNested($dir = 'desc', $nestingKey = 'children');
*/
public function stopWhen($condition);

/**
* Creates a new collection where the items that it will contain are the
* concatenation of the lists of items generated by the transformer function
* after passing each of the items form the original collection.
*
* The transformer function will receive the value and the key for each of the
* items in the collection, in that order, and it must return an array or a
* Traversable object so that it can be concatenated to the final result.
*
* If no transformer function is passed, an "identity" function will be used.
* This is useful when each of the elements in the source collection are
* lists of items to be appended one after another.
*
* ### Example:
*
* {{{
* $items [[1, 2, 3], [4, 5]];
* $unfold = (new Collection($items))->unfold(); // Returns [1, 2, 3, 4, 5]
* }}}
*
* Using a transformer
*
* {{{
* $items [1, 2, 3];
* $allItems = (new Collection($items))->unfold(function ($page) {
* return $service->fetchPage($page)->toArray();
* });
* }}}
*
* @param callable|array $transformer A callable function that will receive each of
* the items in the collection and should return an array or Traversable object
* @return \Cake\Collection\CollectionInterface
*/
public function unfold(callable $transformer = null);

}
12 changes: 8 additions & 4 deletions src/Collection/CollectionTrait.php
Expand Up @@ -441,16 +441,20 @@ public function stopWhen($condition) {
return new StoppableIterator($this, $condition);
}

public function unfold(callable $unfolder = null) {
if ($unfolder === null) {
$unfolder = function ($item) {
/**
* {@inheritDoc}
*
*/
public function unfold(callable $transformer = null) {
if ($transformer === null) {
$transformer = function ($item) {
return $item;
};
}

return new Collection(
new RecursiveIteratorIterator(
new UnfoldIterator($this, $unfolder), RecursiveIteratorIterator::LEAVES_ONLY
new UnfoldIterator($this, $transformer), RecursiveIteratorIterator::LEAVES_ONLY
)
);
}
Expand Down
44 changes: 44 additions & 0 deletions src/Collection/Iterator/NoChildrenIterator.php
@@ -0,0 +1,44 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Collection\Iterator;

use Cake\Collection\Collection;
use RecursiveIterator;

/**
* An iterator that can be used as argument for other iterators that require
* a RecursiveIterator, but that will always report as having no nested items.
*/
class NoChildrenIterator extends Collection implements RecursiveIterator {

/**
* Returns false as there are no children iterators in this collection
*
* @return bool
*/
public function hasChildren() {
return false;
}

/**
* Returns null as there are no children for this iteration level
*
* @return null
*/
public function getChildren() {
return null;
}

}
38 changes: 22 additions & 16 deletions src/Collection/Iterator/UnfoldIterator.php
Expand Up @@ -14,36 +14,54 @@
*/
namespace Cake\Collection\Iterator;

use Cake\Collection\Collection;
use Cake\Collection\Iterator\NoChildrenIterator;
use IteratorIterator;
use RecursiveIterator;

/**
*
* An iterator that can be used to generate nested iterators out of each of
* applying an function to each of the elements in this iterator.
*/
class UnfoldIterator extends IteratorIterator implements RecursiveIterator {

/**
*
* A functions that gets passed each of the elements of this iterator and
* that must return an array or Traversable object.
*
* @var callable
*/
protected $_unfolder;

/**
* Creates the iterator that will genere children iterators out of each of the
* elements it was constructed with.
*
* @param array|\Traversable $items The list of values to iterate
* @param callable $unfolder
* @param callable $unfolder A callable function that will receive the
* current item and key. It must return an array or Traversable object
* out of which the nested iterators will be yielded.
*/
public function __construct($items, callable $unfolder) {
$this->_unfolder = $unfolder;
parent::__construct($items);
}

/**
* Returns true as each of the elements in the array represent a
* list of items
*
* @return bool
*/
public function hasChildren() {
return true;
}

/**
* Returns an iterator containing the items generated out of transforming
* the current value with the callable function.
*
* @return \RecursiveIterator
*/
public function getChildren() {
$current = $this->current();
$key = $this->key();
Expand All @@ -53,15 +71,3 @@ public function getChildren() {
}

}

class NoChildrenIterator extends Collection implements RecursiveIterator {

public function hasChildren() {
return false;
}

public function getChildren() {
return null;
}

}
41 changes: 41 additions & 0 deletions tests/TestCase/Collection/CollectionTest.php
Expand Up @@ -995,6 +995,11 @@ public function testStopWhenWithArray() {
$this->assertEquals([['foo' => 'bar']], $collection->toArray());
}

/**
* Tests the unfold method
*
* @return void
*/
public function testUnfold() {
$items = [
[1, 2, 3, 4],
Expand All @@ -1004,6 +1009,42 @@ public function testUnfold() {

$collection = (new Collection($items))->unfold();
$this->assertEquals(range(1, 8), $collection->toArray(false));

$items = [
[1, 2],
new Collection([3, 4])
];
$collection = (new Collection($items))->unfold();
$this->assertEquals(range(1, 4), $collection->toArray(false));
}

/**
* Tests the unfold method with empty levels
*
* @return void
*/
public function testUnfoldEmptyLevels() {
$items = [[], [1 , 2], []];
$collection = (new Collection($items))->unfold();
$this->assertEquals(range(1, 2), $collection->toArray(false));

$items = [];
$collection = (new Collection($items))->unfold();
$this->assertEmpty($collection->toArray(false));
}

/**
* Tests the unfold when passing a callable
*
* @return void
*/
public function testUnfoldWithCallable() {
$items = [1, 2, 3];
$collection = (new Collection($items))->unfold(function ($item) {
return range($item, $item * 2);
});
$expected = [1, 2, 2, 3, 4, 3, 4, 5, 6];
$this->assertEquals($expected, $collection->toArray(false));
}

}

0 comments on commit 5fe3e34

Please sign in to comment.