From e72127e359b8546e79816b80014e8a785d3f7125 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 25 Jan 2012 20:34:43 -0500 Subject: [PATCH] WIP, adding sort() and remove() --- lib/Cake/Test/Case/Utility/Set2Test.php | 195 +++++++++++++++++++++++- lib/Cake/Utility/Set2.php | 86 ++++++++++- 2 files changed, 277 insertions(+), 4 deletions(-) diff --git a/lib/Cake/Test/Case/Utility/Set2Test.php b/lib/Cake/Test/Case/Utility/Set2Test.php index 264815cc2..27150224c 100644 --- a/lib/Cake/Test/Case/Utility/Set2Test.php +++ b/lib/Cake/Test/Case/Utility/Set2Test.php @@ -626,7 +626,7 @@ class Set2Test extends CakeTestCase { * * @return void */ - public function testExtractNumbericMixedKeys() { + public function testExtractNumericMixedKeys() { $data = array( 'User' => array( 0 => array( @@ -796,4 +796,197 @@ class Set2Test extends CakeTestCase { $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'])); + } + } diff --git a/lib/Cake/Utility/Set2.php b/lib/Cake/Utility/Set2.php index 85f0b02ae..d7bdea905 100644 --- a/lib/Cake/Utility/Set2.php +++ b/lib/Cake/Utility/Set2.php @@ -23,8 +23,8 @@ App::uses('String', 'Utility'); * * `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,8 +137,9 @@ class Set2 { // any numeric key foreach ($item as $k => $v) { if (is_numeric($k)) { - $next[] = $v; + $next[] =& $v; } + unset($v); } } elseif ($token === '{s}') { // any string key @@ -146,6 +147,7 @@ class Set2 { if (is_string($k)) { $next[] = $v; } + unset($v); } } elseif (is_numeric($token)) { // numeric keys like 0, 1, 2 @@ -153,6 +155,7 @@ class Set2 { if ($k == $token) { $next[] = $v; } + unset($v); } } else { // bare string key @@ -161,6 +164,7 @@ class Set2 { if ($k === $token) { $next[] = $v; } + unset($v); } } } @@ -241,11 +245,33 @@ class Set2 { } 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 @@ class Set2 { } +/** + * 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; } /**