From e736ea3af98bb4b6f0f33f6b30567f1d3af48d0f Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 15 Jan 2012 21:38:13 -0500 Subject: [PATCH] Move merge() into Set2. --- lib/Cake/Test/Case/Utility/Set2Test.php | 171 ++++++++++++++++++++++++ lib/Cake/Utility/Set2.php | 57 +++++++- 2 files changed, 226 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Test/Case/Utility/Set2Test.php b/lib/Cake/Test/Case/Utility/Set2Test.php index 3be7b1676..45b5f650d 100644 --- a/lib/Cake/Test/Case/Utility/Set2Test.php +++ b/lib/Cake/Test/Case/Utility/Set2Test.php @@ -276,4 +276,175 @@ class Set2Test extends CakeTestCase { $this->assertEquals($expected, $result); } +/** + * Test diff(); + * + * @return void + */ + public function testDiff() { + $a = array( + 0 => array('name' => 'main'), + 1 => array('name' => 'about') + ); + $b = array( + 0 => array('name' => 'main'), + 1 => array('name' => 'about'), + 2 => array('name' => 'contact') + ); + + $result = Set2::diff($a, array()); + $expected = $a; + $this->assertEquals($expected, $result); + + $result = Set2::diff(array(), $b); + $expected = $b; + $this->assertEquals($expected, $result); + + $result = Set2::diff($a, $b); + $expected = array( + 2 => array('name' => 'contact') + ); + $this->assertEquals($expected, $result); + + + $b = array( + 0 => array('name' => 'me'), + 1 => array('name' => 'about') + ); + + $result = Set2::diff($a, $b); + $expected = array( + 0 => array('name' => 'main') + ); + $this->assertEquals($expected, $result); + + $a = array(); + $b = array('name' => 'bob', 'address' => 'home'); + $result = Set2::diff($a, $b); + $this->assertEquals($result, $b); + + + $a = array('name' => 'bob', 'address' => 'home'); + $b = array(); + $result = Set2::diff($a, $b); + $this->assertEquals($result, $a); + + $a = array('key' => true, 'another' => false, 'name' => 'me'); + $b = array('key' => 1, 'another' => 0); + $expected = array('name' => 'me'); + $result = Set2::diff($a, $b); + $this->assertEquals($expected, $result); + + $a = array('key' => 'value', 'another' => null, 'name' => 'me'); + $b = array('key' => 'differentValue', 'another' => null); + $expected = array('key' => 'value', 'name' => 'me'); + $result = Set2::diff($a, $b); + $this->assertEquals($expected, $result); + + $a = array('key' => 'value', 'another' => null, 'name' => 'me'); + $b = array('key' => 'differentValue', 'another' => 'value'); + $expected = array('key' => 'value', 'another' => null, 'name' => 'me'); + $result = Set2::diff($a, $b); + $this->assertEquals($expected, $result); + + $a = array('key' => 'value', 'another' => null, 'name' => 'me'); + $b = array('key' => 'differentValue', 'another' => 'value'); + $expected = array('key' => 'differentValue', 'another' => 'value', 'name' => 'me'); + $result = Set2::diff($b, $a); + $this->assertEquals($expected, $result); + + $a = array('key' => 'value', 'another' => null, 'name' => 'me'); + $b = array(0 => 'differentValue', 1 => 'value'); + $expected = $a + $b; + $result = Set2::diff($a, $b); + $this->assertEquals($expected, $result); + } + +/** + * Test merge() + * + * @return void + */ + public function testMerge() { + $result = Set2::merge(array('foo'), array('bar')); + $this->assertEquals($result, array('foo', 'bar')); + + $result = Set2::merge(array('foo'), array('user' => 'bob', 'no-bar'), 'bar'); + $this->assertEquals($result, array('foo', 'user' => 'bob', 'no-bar', 'bar')); + + $a = array('foo', 'foo2'); + $b = array('bar', 'bar2'); + $expected = array('foo', 'foo2', 'bar', 'bar2'); + $this->assertEquals($expected, Set2::merge($a, $b)); + + $a = array('foo' => 'bar', 'bar' => 'foo'); + $b = array('foo' => 'no-bar', 'bar' => 'no-foo'); + $expected = array('foo' => 'no-bar', 'bar' => 'no-foo'); + $this->assertEquals($expected, Set2::merge($a, $b)); + + $a = array('users' => array('bob', 'jim')); + $b = array('users' => array('lisa', 'tina')); + $expected = array('users' => array('bob', 'jim', 'lisa', 'tina')); + $this->assertEquals($expected, Set2::merge($a, $b)); + + $a = array('users' => array('jim', 'bob')); + $b = array('users' => 'none'); + $expected = array('users' => 'none'); + $this->assertEquals($expected, Set2::merge($a, $b)); + + $a = array('users' => array('lisa' => array('id' => 5, 'pw' => 'secret')), 'cakephp'); + $b = array('users' => array('lisa' => array('pw' => 'new-pass', 'age' => 23)), 'ice-cream'); + $expected = array( + 'users' => array('lisa' => array('id' => 5, 'pw' => 'new-pass', 'age' => 23)), + 'cakephp', + 'ice-cream' + ); + $result = Set2::merge($a, $b); + $this->assertEquals($expected, $result); + + $c = array( + 'users' => array('lisa' => array('pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog')), + 'chocolate' + ); + $expected = array( + 'users' => array('lisa' => array('id' => 5, 'pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog')), + 'cakephp', + 'ice-cream', + 'chocolate' + ); + $this->assertEquals($expected, Set2::merge($a, $b, $c)); + + $this->assertEquals($expected, Set2::merge($a, $b, array(), $c)); + + $a = array( + 'Tree', + 'CounterCache', + 'Upload' => array( + 'folder' => 'products', + 'fields' => array('image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id') + ) + ); + $b = array( + 'Cacheable' => array('enabled' => false), + 'Limit', + 'Bindable', + 'Validator', + 'Transactional' + ); + $expected = array( + 'Tree', + 'CounterCache', + 'Upload' => array( + 'folder' => 'products', + 'fields' => array('image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id') + ), + 'Cacheable' => array('enabled' => false), + 'Limit', + 'Bindable', + 'Validator', + 'Transactional' + ); + $this->assertEquals(Set2::merge($a, $b), $expected); + } + } diff --git a/lib/Cake/Utility/Set2.php b/lib/Cake/Utility/Set2.php index ea59c9796..5418a33b7 100644 --- a/lib/Cake/Utility/Set2.php +++ b/lib/Cake/Utility/Set2.php @@ -85,7 +85,7 @@ class Set2 { /** * Collapses a multi-dimensional array into a single dimension, using a delimited array path for * each array element's key, i.e. array(array('Foo' => array('Bar' => 'Far'))) becomes - * array('0.Foo.Bar' => 'Far'). + * array('0.Foo.Bar' => 'Far').) * * @param array $data Array to flatten * @param string $separator String used to separate array key elements in a path, defaults to '.' @@ -120,8 +120,36 @@ class Set2 { return $result; } +/** + * This function can be thought of as a hybrid between PHP's `array_merge` and `array_merge_recursive`. + * + * The difference between this method and the built-in ones, is that if an array key contains another array, then + * Set2::merge() will behave in a recursive fashion (unlike `array_merge`). But it will not act recursively for + * keys that contain scalar values (unlike `array_merge_recursive`). + * + * Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays. + * + * @param array $data Array to be merged + * @param mixed $merge Array to merge with. The argument and all trailing arguments will be array cast when merged + * @return array Merged array + * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::merge + */ public static function merge(array $data, $merge) { + $args = func_get_args(); + $return = current($args); + while (($arg = next($args)) !== false) { + foreach ((array)$arg as $key => $val) { + if (!empty($return[$key]) && is_array($return[$key]) && is_array($val)) { + $return[$key] = self::merge($return[$key], $val); + } elseif (is_int($key)) { + $return[] = $val; + } else { + $return[$key] = $val; + } + } + } + return $return; } /** @@ -183,8 +211,33 @@ class Set2 { } +/** + * Computes the difference between two complex arrays. + * This method differs from the built-in array_diff() in that it will preserve keys + * and work on multi-dimensional arrays. + * + * @param mixed $data First value + * @param mixed $data2 Second value + * @return array Returns the key => value pairs that are not common in $data and $data2 + * The expression for this function is ($data - $data2) + ($data2 - ($data - $data2)) + * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::diff + */ public static function diff(array $data, $data2) { - + if (empty($data)) { + return (array)$data2; + } + if (empty($data2)) { + return (array)$data; + } + $intersection = array_intersect_key($data, $data2); + while (($key = key($intersection)) !== null) { + if ($data[$key] == $data2[$key]) { + unset($data[$key]); + unset($data2[$key]); + } + next($intersection); + } + return $data + $data2; } public static function normalize(array $data, $assoc = true) {