Browse files

WIP, adding sort() and remove()

  • Loading branch information...
1 parent 31181f5 commit e72127e359b8546e79816b80014e8a785d3f7125 @markstory markstory committed Jan 25, 2012
Showing with 277 additions and 4 deletions.
  1. +194 −1 lib/Cake/Test/Case/Utility/Set2Test.php
  2. +83 −3 lib/Cake/Utility/Set2.php
View
195 lib/Cake/Test/Case/Utility/Set2Test.php
@@ -626,7 +626,7 @@ public function testExtractNumericKey() {
*
* @return void
*/
- public function testExtractNumbericMixedKeys() {
+ public function testExtractNumericMixedKeys() {
$data = array(
'User' => array(
0 => array(
@@ -796,4 +796,197 @@ public function testExtractAttributePattern() {
$this->assertEquals($expected, $result);
}
+/**
+ * testSort method
+ *
+ * @return void
+ */
+ public function testSort() {
+ $this->markTestIncomplete('Not done, sort() is broken.');
+ $a = array(
+ 0 => array(
+ 'Person' => array('name' => 'Jeff'),
+ 'Friend' => array(array('name' => 'Nate'))
+ ),
+ 1 => array(
+ 'Person' => array('name' => 'Tracy'),
+ 'Friend' => array(array('name' => 'Lindsay'))
+ )
+ );
+ $b = array(
+ 0 => array(
+ 'Person' => array('name' => 'Tracy'),
+ 'Friend' => array(array('name' => 'Lindsay'))
+ ),
+ 1 => array(
+ 'Person' => array('name' => 'Jeff'),
+ 'Friend' => array(array('name' => 'Nate'))
+ )
+ );
+ $a = Set2::sort($a, '{n}.Friend.{n}.name', 'asc');
+ $this->assertEquals($a, $b);
+
+ $b = array(
+ 0 => array(
+ 'Person' => array('name' => 'Jeff'),
+ 'Friend' => array(array('name' => 'Nate'))
+ ),
+ 1 => array(
+ 'Person' => array('name' => 'Tracy'),
+ 'Friend' => array(array('name' => 'Lindsay'))
+ )
+ );
+ $a = array(
+ 0 => array(
+ 'Person' => array('name' => 'Tracy'),
+ 'Friend' => array(array('name' => 'Lindsay'))
+ ),
+ 1 => array(
+ 'Person' => array('name' => 'Jeff'),
+ 'Friend' => array(array('name' => 'Nate'))
+ )
+ );
+ $a = Set2::sort($a, '{n}.Friend.{n}.name', 'desc');
+ $this->assertEquals($a, $b);
+
+ $a = array(
+ 0 => array(
+ 'Person' => array('name' => 'Jeff'),
+ 'Friend' => array(array('name' => 'Nate'))
+ ),
+ 1 => array(
+ 'Person' => array('name' => 'Tracy'),
+ 'Friend' => array(array('name' => 'Lindsay'))
+ ),
+ 2 => array(
+ 'Person' => array('name' => 'Adam'),
+ 'Friend' => array(array('name' => 'Bob'))
+ )
+ );
+ $b = array(
+ 0 => array(
+ 'Person' => array('name' => 'Adam'),
+ 'Friend' => array(array('name' => 'Bob'))
+ ),
+ 1 => array(
+ 'Person' => array('name' => 'Jeff'),
+ 'Friend' => array(array('name' => 'Nate'))
+ ),
+ 2 => array(
+ 'Person' => array('name' => 'Tracy'),
+ 'Friend' => array(array('name' => 'Lindsay'))
+ )
+ );
+ $a = Set2::sort($a, '{n}.Person.name', 'asc');
+ $this->assertEquals($a, $b);
+
+ $a = array(
+ array(7,6,4),
+ array(3,4,5),
+ array(3,2,1),
+ );
+
+ $b = array(
+ array(3,2,1),
+ array(3,4,5),
+ array(7,6,4),
+ );
+
+ $a = Set2::sort($a, '{n}.{n}', 'asc');
+ $this->assertEquals($a, $b);
+
+ $a = array(
+ array(7,6,4),
+ array(3,4,5),
+ array(3,2,array(1,1,1)),
+ );
+
+ $b = array(
+ array(3,2,array(1,1,1)),
+ array(3,4,5),
+ array(7,6,4),
+ );
+
+ $a = Set2::sort($a, '{n}', 'asc');
+ $this->assertEquals($a, $b);
+
+ $a = array(
+ 0 => array('Person' => array('name' => 'Jeff')),
+ 1 => array('Shirt' => array('color' => 'black'))
+ );
+ $b = array(
+ 0 => array('Shirt' => array('color' => 'black')),
+ 1 => array('Person' => array('name' => 'Jeff')),
+ );
+ $a = Set2::sort($a, '{n}.Person.name', 'ASC');
+ $this->assertEquals($a, $b);
+
+ $names = array(
+ array('employees' => array(
+ array('name' => array('first' => 'John', 'last' => 'Doe')))
+ ),
+ array('employees' => array(
+ array('name' => array('first' => 'Jane', 'last' => 'Doe')))
+ ),
+ array('employees' => array(array('name' => array()))),
+ array('employees' => array(array('name' => array())))
+ );
+ $result = Set2::sort($names, '{n}.employees.0.name', 'asc', 1);
+ $expected = array(
+ array('employees' => array(
+ array('name' => array('first' => 'John', 'last' => 'Doe')))
+ ),
+ array('employees' => array(
+ array('name' => array('first' => 'Jane', 'last' => 'Doe')))
+ ),
+ array('employees' => array(array('name' => array()))),
+ array('employees' => array(array('name' => array())))
+ );
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * test sorting with out of order keys.
+ *
+ * @return void
+ */
+ public function testSortWithOutOfOrderKeys() {
+ $data = array(
+ 9 => array('class' => 510, 'test2' => 2),
+ 1 => array('class' => 500, 'test2' => 1),
+ 2 => array('class' => 600, 'test2' => 2),
+ 5 => array('class' => 625, 'test2' => 4),
+ 0 => array('class' => 605, 'test2' => 3),
+ );
+ $expected = array(
+ array('class' => 500, 'test2' => 1),
+ array('class' => 510, 'test2' => 2),
+ array('class' => 600, 'test2' => 2),
+ array('class' => 605, 'test2' => 3),
+ array('class' => 625, 'test2' => 4),
+ );
+ $result = Set2::sort($data, '{n}.class', 'asc');
+ $this->assertEquals($expected, $result);
+
+ $result = Set2::sort($data, '{n}.test2', 'asc');
+ $this->assertEquals($expected, $result);
+ }
+
+
+/**
+ * Test remove()
+ *
+ * @return void
+ */
+ public function testRemove() {
+ $data = self::articleData();
+
+ $result = Set2::insert($data, '{n}.Article', array('test'));
+ debug($result);
+
+ $result = Set2::remove($data, '{n}.Article');
+ debug($result);
+ $this->assertFalse(isset($data[0]['Article']));
+ }
+
}
View
86 lib/Cake/Utility/Set2.php
@@ -23,8 +23,8 @@
*
* `Set2` provides an improved interface and more consistent and
* predictable set of features over `Set`. While it lacks the spotty
- * support for pseudo Xpath, its more fully feature dot notation provides
- * the same utility.
+ * support for pseudo Xpath, its more fully featured dot notation provides
+ * the similar features in a more consistent way.
*
* @package Cake.Utility
*/
@@ -137,22 +137,25 @@ protected static function _traverse(array &$data, $path, $callback) {
// any numeric key
foreach ($item as $k => $v) {
if (is_numeric($k)) {
- $next[] = $v;
+ $next[] =& $v;
}
+ unset($v);
}
} elseif ($token === '{s}') {
// any string key
foreach ($item as $k => $v) {
if (is_string($k)) {
$next[] = $v;
}
+ unset($v);
}
} elseif (is_numeric($token)) {
// numeric keys like 0, 1, 2
foreach ($item as $k => $v) {
if ($k == $token) {
$next[] = $v;
}
+ unset($v);
}
} else {
// bare string key
@@ -161,6 +164,7 @@ protected static function _traverse(array &$data, $path, $callback) {
if ($k === $token) {
$next[] = $v;
}
+ unset($v);
}
}
}
@@ -241,11 +245,33 @@ protected static function _matches(array $data, $selector) {
}
public static function insert(array $data, $path, $values = null) {
+ if (empty($path)) {
+ return $data;
+ }
+ $result = self::_traverse($data, $path, function (&$value) use ($values) {
+ $value['test'] = $values;
+ return $value;
+ });
+
+ return $data;
}
+/**
+ * Remove data matching $path from the $data array.
+ *
+ * @param array $data The data to operate on
+ * @param string $path A path expression to use to remove.
+ * @return array The modified array.
+ */
public static function remove(array $data, $path) {
+ if (empty($path)) {
+ return $data;
+ }
+ return self::_traverse($data, $path, function ($value) {
+ return $value;
+ });
}
public static function combine(array $data, $keyPath, $valuePath = null) {
@@ -464,8 +490,62 @@ public static function map(array $data, $path, $function = null) {
}
+/**
+ * Sorts an array by any value, determined by a Set-compatible path
+ *
+ * @param array $data An array of data to sort
+ * @param string $path A Set-compatible path to the array value
+ * @param string $dir Direction of sorting - either ascending (ASC), or descending (DESC)
+ * @return array Sorted array of data
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::sort
+ */
public static function sort(array $data, $path, $dir) {
+ $originalKeys = array_keys($data);
+ if (is_numeric(implode('', $originalKeys))) {
+ $data = array_values($data);
+ }
+ $result = self::_squash(self::extract($data, $path));
+ $keys = self::extract($result, '{n}.id');
+ $values = self::extract($result, '{n}.value');
+
+ $dir = strtolower($dir);
+ if ($dir === 'asc') {
+ $dir = SORT_ASC;
+ } elseif ($dir === 'desc') {
+ $dir = SORT_DESC;
+ }
+ array_multisort($values, $dir, $keys, $dir);
+ $sorted = array();
+ $keys = array_unique($keys);
+ foreach ($keys as $k) {
+ $sorted[] = $data[$k];
+ }
+ return $sorted;
+ }
+
+/**
+ * Helper method for sort()
+ * Sqaushes an array to a single hash so it can be sorted.
+ *
+ * @param array $data The data to squash.
+ * @param string $key The key for the data.
+ * @return array
+ */
+ protected static function _squash($data, $key = null) {
+ $stack = array();
+ foreach ($data as $k => $r) {
+ $id = $k;
+ if (!is_null($key)) {
+ $id = $key;
+ }
+ if (is_array($r) && !empty($r)) {
+ $stack = array_merge($stack, self::_squash($r, $id));
+ } else {
+ $stack[] = array('id' => $id, 'value' => $r);
+ }
+ }
+ return $stack;
}
/**

0 comments on commit e72127e

Please sign in to comment.