diff --git a/lib/Cake/Test/Case/Utility/HashTest.php b/lib/Cake/Test/Case/Utility/HashTest.php index c4a43df9d65..fb5db2693c0 100644 --- a/lib/Cake/Test/Case/Utility/HashTest.php +++ b/lib/Cake/Test/Case/Utility/HashTest.php @@ -1356,7 +1356,7 @@ public function testSortNaturalIgnoreCase() { ); $this->assertEquals($expected, $result); - $result = Hash::sort($items, '{n}.Item.image', 'asc', array('type' => 'natural', 'ignoreCase' => true)); + $result = Hash::sort($items, '{n}.Item.image', 'asc', array('type' => 'NATURAL', 'ignoreCase' => true)); $expected = array( array('Item' => array('image' => 'img1.jpg')), array('Item' => array('image' => 'img2.jpg')), @@ -1529,6 +1529,58 @@ public function testSortRegularIgnoreCase() { $this->assertEquals($expected, $sorted); } +/** + * Test sorting on a nested key that is sometimes undefined. + * + * @return void + */ + public function testSortSparse() { + $data = array( + array( + 'id' => 1, + 'title' => 'element 1', + 'extra' => 1, + ), + array( + 'id' => 2, + 'title' => 'element 2', + 'extra' => 2, + ), + array( + 'id' => 3, + 'title' => 'element 3', + ), + array( + 'id' => 4, + 'title' => 'element 4', + 'extra' => 4, + ) + ); + $result = Hash::sort($data, '{n}.extra', 'desc', 'natural'); + $expected = array( + array( + 'id' => 4, + 'title' => 'element 4', + 'extra' => 4, + ), + array( + 'id' => 2, + 'title' => 'element 2', + 'extra' => 2, + ), + array( + 'id' => 1, + 'title' => 'element 1', + 'extra' => 1, + ), + array( + 'id' => 3, + 'title' => 'element 3', + ), + ); + $this->assertSame($expected, $result); + } + /** * Test insert() * @@ -1594,6 +1646,17 @@ public function testInsertMulti() { 4 => array('Item' => array('id' => 5, 'title' => 'fifth')), ); $this->assertEquals($expected, $result); + + $data[3]['testable'] = true; + $result = Hash::insert($data, '{n}[testable].Item[id=/\b2|\b4/].test', 2); + $expected = array( + 0 => array('Item' => array('id' => 1, 'title' => 'first')), + 1 => array('Item' => array('id' => 2, 'title' => 'second')), + 2 => array('Item' => array('id' => 3, 'title' => 'third')), + 3 => array('Item' => array('id' => 4, 'title' => 'fourth', 'test' => 2), 'testable' => true), + 4 => array('Item' => array('id' => 5, 'title' => 'fifth')), + ); + $this->assertEquals($expected, $result); } /** @@ -1686,6 +1749,43 @@ public function testRemove() { $this->assertEquals($expected, $result); $result = Hash::remove($array, '{n}.{n}.part'); $this->assertEquals($expected, $result); + + $array = array( + 'foo' => 'string', + ); + $expected = $array; + $result = Hash::remove($array, 'foo.bar'); + $this->assertEquals($expected, $result); + + $array = array( + 'foo' => 'string', + 'bar' => array( + 0 => 'a', + 1 => 'b', + ), + ); + $expected = array( + 'foo' => 'string', + 'bar' => array( + 1 => 'b', + ), + ); + $result = Hash::remove($array, '{s}.0'); + $this->assertEquals($expected, $result); + + $array = array( + 'foo' => array( + 0 => 'a', + 1 => 'b', + ), + ); + $expected = array( + 'foo' => array( + 1 => 'b', + ), + ); + $result = Hash::remove($array, 'foo[1=b].0'); + $this->assertEquals($expected, $result); } /** @@ -1721,6 +1821,17 @@ public function testRemoveMulti() { 4 => array('Item' => array('id' => 5, 'title' => 'fifth')), ); $this->assertEquals($expected, $result); + + $data[3]['testable'] = true; + $result = Hash::remove($data, '{n}[testable].Item[id=/\b2|\b4/].title'); + $expected = array( + 0 => array('Item' => array('id' => 1, 'title' => 'first')), + 1 => array('Item' => array('id' => 2, 'title' => 'second')), + 2 => array('Item' => array('id' => 3, 'title' => 'third')), + 3 => array('Item' => array('id' => 4), 'testable' => true), + 4 => array('Item' => array('id' => 5, 'title' => 'fifth')), + ); + $this->assertEquals($expected, $result); } /** diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index 6208aa4a4b5..08dd2700a52 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -275,12 +275,10 @@ public static function insert(array $data, $path, $values = null) { foreach ($data as $k => $v) { if (static::_matchToken($k, $token)) { - if ($conditions && static::_matches($v, $conditions)) { - $data[$k] = array_merge($v, $values); - continue; - } - if (!$conditions) { - $data[$k] = static::insert($v, $nextPath, $values); + if (!$conditions || static::_matches($v, $conditions)) { + $data[$k] = $nextPath + ? static::insert($v, $nextPath, $values) + : array_merge($v, (array)$values); } } } @@ -302,9 +300,6 @@ protected static function _simpleOp($op, $data, $path, $values = null) { $count = count($path); $last = $count - 1; foreach ($path as $i => $key) { - if ((is_numeric($key) && intval($key) > 0 || $key === '0') && strpos($key, '0') !== 0) { - $key = (int)$key; - } if ($op === 'insert') { if ($i === $last) { $_list[$key] = $values; @@ -319,7 +314,9 @@ protected static function _simpleOp($op, $data, $path, $values = null) { } } elseif ($op === 'remove') { if ($i === $last) { - unset($_list[$key]); + if (is_array($_list)) { + unset($_list[$key]); + } return $data; } if (!isset($_list[$key])) { @@ -359,15 +356,21 @@ public static function remove(array $data, $path) { foreach ($data as $k => $v) { $match = static::_matchToken($k, $token); if ($match && is_array($v)) { - if ($conditions && static::_matches($v, $conditions)) { - unset($data[$k]); - continue; + if ($conditions) { + if (static::_matches($v, $conditions)) { + if ($nextPath !== '') { + $data[$k] = static::remove($v, $nextPath); + } else { + unset($data[$k]); + } + } + } else { + $data[$k] = static::remove($v, $nextPath); } - $data[$k] = static::remove($v, $nextPath); if (empty($data[$k])) { unset($data[$k]); } - } elseif ($match && empty($nextPath)) { + } elseif ($match && $nextPath === '') { unset($data[$k]); } } @@ -873,12 +876,18 @@ public static function sort(array $data, $path, $dir = 'asc', $type = 'regular') $data = array_values($data); } $sortValues = static::extract($data, $path); - $sortCount = count($sortValues); $dataCount = count($data); // Make sortValues match the data length, as some keys could be missing // the sorted value path. - if ($sortCount < $dataCount) { + $missingData = count($sortValues) < $dataCount; + if ($missingData && $numeric) { + // Get the path without the leading '{n}.' + $itemPath = substr($path, 4); + foreach ($data as $key => $value) { + $sortValues[$key] = static::get($value, $itemPath); + } + } elseif ($missingData) { $sortValues = array_pad($sortValues, $dataCount, null); } $result = static::_squash($sortValues); @@ -893,9 +902,8 @@ public static function sort(array $data, $path, $dir = 'asc', $type = 'regular') $type += array('ignoreCase' => false, 'type' => 'regular'); $ignoreCase = $type['ignoreCase']; $type = $type['type']; - } else { - $type = strtolower($type); } + $type = strtolower($type); if ($type === 'natural' && version_compare(PHP_VERSION, '5.4.0', '<')) { $type = 'regular';