Skip to content

Commit

Permalink
Implemented Collection::extract()
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Dec 26, 2013
1 parent 7b7118e commit cab6f5e
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 6 deletions.
14 changes: 14 additions & 0 deletions Cake/Test/TestCase/Utility/CollectionTest.php
Expand Up @@ -241,4 +241,18 @@ public function testReduce() {
->will($this->returnValue(16));
$this->assertEquals(16, $collection->reduce($callable, 10));
}

/**
* Tests extract
*
* @return void
*/
public function testExtract() {
$items = [['a' => ['b' => ['c' => 1]]], 2];
$collection = new Collection($items);
$map = $collection->extract('a.b.c');
$this->assertInstanceOf('\Cake\Utility\Iterator\ExtractIterator', $map);
$this->assertEquals([1, null], iterator_to_array($map));
}

}
83 changes: 83 additions & 0 deletions Cake/Test/TestCase/Utility/Iterator/ExtractIteratorTest.php
@@ -0,0 +1,83 @@
<?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 CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Test\TestCase\Utility\Iterator;

use ArrayObject;
use Cake\TestSuite\TestCase;
use Cake\Utility\Iterator\ExtractIterator;

/**
* ExtractIterator Test
*
*/
class ExtractIteratorTest extends TestCase {

/**
* Tests it is possible to extract a column in the first level of an array
*
* @return void
*/
public function testExtractFromArrayShallow() {
$items = [
['a' => 1, 'b' =>2],
['a' => 3, 'b' => 4]
];
$extractor = new ExtractIterator($items, 'a');
$this->assertEquals([1, 3], iterator_to_array($extractor));

$extractor = new ExtractIterator($items, 'b');
$this->assertEquals([2, 4], iterator_to_array($extractor));

$extractor = new ExtractIterator($items, 'c');
$this->assertEquals([null, null], iterator_to_array($extractor));
}

/**
* Tests it is possible to extract a column in the first level of an object
*
* @return void
*/
public function testExtractFromObjectShallow() {
$items = [
new ArrayObject(['a' => 1, 'b' =>2]),
new ArrayObject(['a' => 3, 'b' => 4])
];
$extractor = new ExtractIterator($items, 'a');
$this->assertEquals([1, 3], iterator_to_array($extractor));

$extractor = new ExtractIterator($items, 'b');
$this->assertEquals([2, 4], iterator_to_array($extractor));

$extractor = new ExtractIterator($items, 'c');
$this->assertEquals([null, null], iterator_to_array($extractor));
}

/**
* Tests it is possible to extract a column deeply nested in the structure
*
* @return void
*/
public function testExtractFromArrayDeep() {
$items = [
['a' => ['b' => ['c' => 10]], 'b' =>2],
['a' => ['b' => ['d' => 15]], 'b' => 4],
['a' => ['x' => ['z' => 20]], 'b' => 4],
['a' => ['b' => ['c' => 25]], 'b' =>2],
];
$extractor = new ExtractIterator($items, 'a.b.c');
$this->assertEquals([10, null, null, 25], iterator_to_array($extractor));
}

}
35 changes: 29 additions & 6 deletions Cake/Utility/Collection.php
Expand Up @@ -15,6 +15,7 @@
namespace Cake\Utility;

use ArrayIterator;
use Cake\Utility\Iterator\ExtractIterator;
use Cake\Utility\Iterator\FilterIterator;
use Cake\Utility\Iterator\ReplaceIterator;
use InvalidArgumentException;
Expand Down Expand Up @@ -241,12 +242,34 @@ public function reduce(callable $c, $zero) {
return $result;
}

public function mapReduce(callable $map, callable $reduce) {
}


public function extract($property) {

/**
* Returns a new collection containing the column or property value found in each
* of th elements, as requested in the $matcher param.
*
* The matcher can be a string with a property name to extract or a dot separated
* path of properties that should be followed to get the last one in the path.
*
* If a column or property could not be found for a particular element in the
* collection, that position is filled with null.
*
* ### Example:
*
* Extract the user name for all comments in the array:
*
* {{{
* $items = [
* ['comment' => ['body' => 'cool', 'user' => ['name' => 'Mark']],
* ['comment' => ['body' => 'very cool', 'user' => ['name' => 'Renan']]
* ];
* $extractor = new ExtractIterator($items, 'comment.user.name'');
* }}}
*
* @param string $path a dot separated string symbolizing the path to follow
* inside the hierarchy of each value so that the column can be extracted.
* @return \Cake\Utility\Iterator\ExtractIterator
*/
public function extract($matcher) {
return new ExtractIterator($this, $matcher);
}

public function max() {
Expand Down
79 changes: 79 additions & 0 deletions Cake/Utility/Iterator/ExtractIterator.php
@@ -0,0 +1,79 @@
<?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 CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Utility\Iterator;

use Cake\Utility\Collection;

/**
* Creates an iterator from another iterator that extract the requested column
* or property based on a path
*/
class ExtractIterator extends Collection {

/**
* A path to follow inside a hierarchy in order to get a particular property,
* which name is the last in this array
*
* @var array
*/
protected $_path;


/**
* Creates the iterator that will return the requested property for each value
* in the collection expressed in $path
*
* ### Example:
*
* Extract the user name for all comments in the array:
*
* {{{
* $items = [
* ['comment' => ['body' => 'cool', 'user' => ['name' => 'Mark']],
* ['comment' => ['body' => 'very cool', 'user' => ['name' => 'Renan']]
* ];
* $extractor = new ExtractIterator($items, 'comment.user.name'');
* }}}
*
* @param array|\Traversable $items The list of values to iterate
* @param string $path a dot separated string symbolizing the path to follow
* inside the hierarchy of each value so that the column can be extracted.
* @return void
*/
public function __construct($items, $path) {
$this->_path = explode('.', $path);
parent::__construct($items);
}

/**
* Returns the column value defined in $path or null if the path could not be
* followed
*
* @return mixed
*/
public function current() {
$current = parent::current();
$value = null;
foreach ($this->_path as $column) {
if (!isset($current[$column])) {
return null;
}
$value = $current[$column];
$current = $value;
}
return $value;
}

}

0 comments on commit cab6f5e

Please sign in to comment.