Skip to content

Commit cab6f5e

Browse files
committed
Implemented Collection::extract()
1 parent 7b7118e commit cab6f5e

File tree

4 files changed

+205
-6
lines changed

4 files changed

+205
-6
lines changed

Cake/Test/TestCase/Utility/CollectionTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,18 @@ public function testReduce() {
241241
->will($this->returnValue(16));
242242
$this->assertEquals(16, $collection->reduce($callable, 10));
243243
}
244+
245+
/**
246+
* Tests extract
247+
*
248+
* @return void
249+
*/
250+
public function testExtract() {
251+
$items = [['a' => ['b' => ['c' => 1]]], 2];
252+
$collection = new Collection($items);
253+
$map = $collection->extract('a.b.c');
254+
$this->assertInstanceOf('\Cake\Utility\Iterator\ExtractIterator', $map);
255+
$this->assertEquals([1, null], iterator_to_array($map));
256+
}
257+
244258
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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 CakePHP(tm) v 3.0.0
13+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14+
*/
15+
namespace Cake\Test\TestCase\Utility\Iterator;
16+
17+
use ArrayObject;
18+
use Cake\TestSuite\TestCase;
19+
use Cake\Utility\Iterator\ExtractIterator;
20+
21+
/**
22+
* ExtractIterator Test
23+
*
24+
*/
25+
class ExtractIteratorTest extends TestCase {
26+
27+
/**
28+
* Tests it is possible to extract a column in the first level of an array
29+
*
30+
* @return void
31+
*/
32+
public function testExtractFromArrayShallow() {
33+
$items = [
34+
['a' => 1, 'b' =>2],
35+
['a' => 3, 'b' => 4]
36+
];
37+
$extractor = new ExtractIterator($items, 'a');
38+
$this->assertEquals([1, 3], iterator_to_array($extractor));
39+
40+
$extractor = new ExtractIterator($items, 'b');
41+
$this->assertEquals([2, 4], iterator_to_array($extractor));
42+
43+
$extractor = new ExtractIterator($items, 'c');
44+
$this->assertEquals([null, null], iterator_to_array($extractor));
45+
}
46+
47+
/**
48+
* Tests it is possible to extract a column in the first level of an object
49+
*
50+
* @return void
51+
*/
52+
public function testExtractFromObjectShallow() {
53+
$items = [
54+
new ArrayObject(['a' => 1, 'b' =>2]),
55+
new ArrayObject(['a' => 3, 'b' => 4])
56+
];
57+
$extractor = new ExtractIterator($items, 'a');
58+
$this->assertEquals([1, 3], iterator_to_array($extractor));
59+
60+
$extractor = new ExtractIterator($items, 'b');
61+
$this->assertEquals([2, 4], iterator_to_array($extractor));
62+
63+
$extractor = new ExtractIterator($items, 'c');
64+
$this->assertEquals([null, null], iterator_to_array($extractor));
65+
}
66+
67+
/**
68+
* Tests it is possible to extract a column deeply nested in the structure
69+
*
70+
* @return void
71+
*/
72+
public function testExtractFromArrayDeep() {
73+
$items = [
74+
['a' => ['b' => ['c' => 10]], 'b' =>2],
75+
['a' => ['b' => ['d' => 15]], 'b' => 4],
76+
['a' => ['x' => ['z' => 20]], 'b' => 4],
77+
['a' => ['b' => ['c' => 25]], 'b' =>2],
78+
];
79+
$extractor = new ExtractIterator($items, 'a.b.c');
80+
$this->assertEquals([10, null, null, 25], iterator_to_array($extractor));
81+
}
82+
83+
}

Cake/Utility/Collection.php

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
namespace Cake\Utility;
1616

1717
use ArrayIterator;
18+
use Cake\Utility\Iterator\ExtractIterator;
1819
use Cake\Utility\Iterator\FilterIterator;
1920
use Cake\Utility\Iterator\ReplaceIterator;
2021
use InvalidArgumentException;
@@ -241,12 +242,34 @@ public function reduce(callable $c, $zero) {
241242
return $result;
242243
}
243244

244-
public function mapReduce(callable $map, callable $reduce) {
245-
}
246-
247-
248-
public function extract($property) {
249-
245+
/**
246+
* Returns a new collection containing the column or property value found in each
247+
* of th elements, as requested in the $matcher param.
248+
*
249+
* The matcher can be a string with a property name to extract or a dot separated
250+
* path of properties that should be followed to get the last one in the path.
251+
*
252+
* If a column or property could not be found for a particular element in the
253+
* collection, that position is filled with null.
254+
*
255+
* ### Example:
256+
*
257+
* Extract the user name for all comments in the array:
258+
*
259+
* {{{
260+
* $items = [
261+
* ['comment' => ['body' => 'cool', 'user' => ['name' => 'Mark']],
262+
* ['comment' => ['body' => 'very cool', 'user' => ['name' => 'Renan']]
263+
* ];
264+
* $extractor = new ExtractIterator($items, 'comment.user.name'');
265+
* }}}
266+
*
267+
* @param string $path a dot separated string symbolizing the path to follow
268+
* inside the hierarchy of each value so that the column can be extracted.
269+
* @return \Cake\Utility\Iterator\ExtractIterator
270+
*/
271+
public function extract($matcher) {
272+
return new ExtractIterator($this, $matcher);
250273
}
251274

252275
public function max() {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 CakePHP(tm) v 3.0.0
13+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14+
*/
15+
namespace Cake\Utility\Iterator;
16+
17+
use Cake\Utility\Collection;
18+
19+
/**
20+
* Creates an iterator from another iterator that extract the requested column
21+
* or property based on a path
22+
*/
23+
class ExtractIterator extends Collection {
24+
25+
/**
26+
* A path to follow inside a hierarchy in order to get a particular property,
27+
* which name is the last in this array
28+
*
29+
* @var array
30+
*/
31+
protected $_path;
32+
33+
34+
/**
35+
* Creates the iterator that will return the requested property for each value
36+
* in the collection expressed in $path
37+
*
38+
* ### Example:
39+
*
40+
* Extract the user name for all comments in the array:
41+
*
42+
* {{{
43+
* $items = [
44+
* ['comment' => ['body' => 'cool', 'user' => ['name' => 'Mark']],
45+
* ['comment' => ['body' => 'very cool', 'user' => ['name' => 'Renan']]
46+
* ];
47+
* $extractor = new ExtractIterator($items, 'comment.user.name'');
48+
* }}}
49+
*
50+
* @param array|\Traversable $items The list of values to iterate
51+
* @param string $path a dot separated string symbolizing the path to follow
52+
* inside the hierarchy of each value so that the column can be extracted.
53+
* @return void
54+
*/
55+
public function __construct($items, $path) {
56+
$this->_path = explode('.', $path);
57+
parent::__construct($items);
58+
}
59+
60+
/**
61+
* Returns the column value defined in $path or null if the path could not be
62+
* followed
63+
*
64+
* @return mixed
65+
*/
66+
public function current() {
67+
$current = parent::current();
68+
$value = null;
69+
foreach ($this->_path as $column) {
70+
if (!isset($current[$column])) {
71+
return null;
72+
}
73+
$value = $current[$column];
74+
$current = $value;
75+
}
76+
return $value;
77+
}
78+
79+
}

0 commit comments

Comments
 (0)