Skip to content

Commit

Permalink
Implementend collection nest
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Jan 19, 2014
1 parent 4de65e7 commit 41ddf1a
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/Collection/CollectionTrait.php
Expand Up @@ -15,6 +15,7 @@
namespace Cake\Collection;

use AppendIterator;
use ArrayObject;
use Cake\Collection\Collection;
use Cake\Collection\Iterator\ExtractIterator;
use Cake\Collection\Iterator\FilterIterator;
Expand Down Expand Up @@ -701,6 +702,54 @@ public function combine($keyPath, $valuePath, $groupPath = null) {
return new Collection(new MapReduce($this, $mapper, $reducer));
}

/**
* Returns a new collection where the values are nested in a tree-like structure
* based on an id property path and a parent id property path.
*
* @param callable|string $idPath the column name path to use for determining
* whther an element is parent of another
* @param callable|string $parentPath the column name path to use for determining
* whther an element is child of another
* @return \Cake\Collection\Collection
*/
public function nest($idPath, $parentPath) {
$parents = [];
$idPath = $this->_propertyExtractor($idPath);
$parentPath = $this->_propertyExtractor($parentPath);
$isObject = !is_array($this->first());

$mapper = function($row, $key, $mapReduce) use (&$parents, $idPath, $parentPath) {
$row['children'] = [];
$id = $idPath($row, $key);
$parentId = $parentPath($row, $key);
$parents[$id] =& $row;
$mapReduce->emitIntermediate($id, $parentId);
};

$reducer = function($values, $key, $mapReduce) use (&$parents, $isObject) {
if (empty($key) || !isset($parents[$key])) {
foreach ($values as $id) {
$parents[$id] = $isObject ? $parents[$id] : new ArrayObject($parents[$id]);
$mapReduce->emit($parents[$id]);
}
return;
}

foreach ($values as $id) {
$parents[$key]['children'][] =& $parents[$id];
}
};

$collection = new MapReduce($this, $mapper, $reducer);
if (!$isObject) {
$collection = (new Collection($collection))->map(function($value) {
return (array)$value;
});
}

return new Collection($collection);
}

/**
* Returns an array representation of the results
*
Expand Down
103 changes: 103 additions & 0 deletions tests/TestCase/Collection/CollectionTest.php
Expand Up @@ -676,4 +676,107 @@ function($value, $key) {
$this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
}

/**
* Tests the nest method with only one level
*
* @return void
*/
public function testNest() {
$items = [
['id' => 1, 'parent_id' => null],
['id' => 2, 'parent_id' => 1],
['id' => 3, 'parent_id' => 1],
['id' => 4, 'parent_id' => 1],
['id' => 5, 'parent_id' => 6],
['id' => 6, 'parent_id' => null],
['id' => 7, 'parent_id' => 1],
['id' => 8, 'parent_id' => 6],
['id' => 9, 'parent_id' => 6],
['id' => 10, 'parent_id' => 6]
];
$collection = (new Collection($items))->nest('id', 'parent_id');
$expected = [
[
'id' => 1,
'parent_id' => null,
'children' => [
['id' => 2, 'parent_id' => 1, 'children' => []],
['id' => 3, 'parent_id' => 1, 'children' => []],
['id' => 4, 'parent_id' => 1, 'children' => []],
['id' => 7, 'parent_id' => 1, 'children' => []]
]
],
[
'id' => 6,
'parent_id' => null,
'children' => [
['id' => 5, 'parent_id' => 6, 'children' => []],
['id' => 8, 'parent_id' => 6, 'children' => []],
['id' => 9, 'parent_id' => 6, 'children' => []],
['id' => 10, 'parent_id' => 6, 'children' => []]
]
]
];
$this->assertEquals($expected, $collection->toArray());
}

/**
* Tests the nest method with more than one level
*
* @return void
*/
public function testNestMultiLevel() {
$items = [
['id' => 1, 'parent_id' => null],
['id' => 2, 'parent_id' => 1],
['id' => 3, 'parent_id' => 2],
['id' => 4, 'parent_id' => 2],
['id' => 5, 'parent_id' => 3],
['id' => 6, 'parent_id' => null],
['id' => 7, 'parent_id' => 3],
['id' => 8, 'parent_id' => 4],
['id' => 9, 'parent_id' => 6],
['id' => 10, 'parent_id' => 6]
];
$collection = (new Collection($items))->nest('id', 'parent_id');
$expected = [
[
'id' => 1,
'parent_id' => null,
'children' => [
[
'id' => 2,
'parent_id' => 1,
'children' => [
[
'id' => 3,
'parent_id' => 2,
'children' => [
['id' => 5, 'parent_id' => 3, 'children' => []],
['id' => 7, 'parent_id' => 3, 'children' => []]
]
],
[
'id' => 4,
'parent_id' => 2,
'children' => [
['id' => 8, 'parent_id' => 4, 'children' => []]
]
]
]
]
]
],
[
'id' => 6,
'parent_id' => null,
'children' => [
['id' => 9, 'parent_id' => 6, 'children' => []],
['id' => 10, 'parent_id' => 6, 'children' => []]
]
]
];
$this->assertEquals($expected, $collection->toArray());
}

}

0 comments on commit 41ddf1a

Please sign in to comment.