WIP, adding sort() and remove()

This commit is contained in:
mark_story 2012-01-25 20:34:43 -05:00
parent 31181f58d6
commit e72127e359
2 changed files with 277 additions and 4 deletions

View file

@ -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']));
}
}

View file

@ -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;
}
/**