From 17deafad3bb716b74e53a4006627c1c5fed6ebe2 Mon Sep 17 00:00:00 2001 From: Tim Otten <totten@civicrm.org> Date: Sun, 19 Jan 2014 16:29:58 -0800 Subject: [PATCH] CRM_Utils_Array::product() - Helper to generate Cartesian products --- CRM/Utils/Array.php | 43 +++++++++++++++++++++++++++ tests/phpunit/CRM/Utils/ArrayTest.php | 41 +++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/CRM/Utils/Array.php b/CRM/Utils/Array.php index c6e8cfb93a..575c0e66b8 100644 --- a/CRM/Utils/Array.php +++ b/CRM/Utils/Array.php @@ -635,5 +635,48 @@ class CRM_Utils_Array { } return $default; } + + /** + * Generate the Cartesian product of zero or more vectors + * + * @param array $dimensions list of dimensions to multiply; each key is a dimension name; each value is a vector + * @param array $template a base set of values included in every output + * @return array each item is a distinct combination of values from $dimensions + * + * For example, the product of + * { + * fg => {red, blue}, + * bg => {white, black} + * } + * would be + * { + * {fg => red, bg => white}, + * {fg => red, bg => black}, + * {fg => blue, bg => white}, + * {fg => blue, bg => black} + * } + */ + static function product($dimensions, $template = array()) { + if (empty($dimensions)) { + return array($template); + } + + foreach ($dimensions as $key => $value) { + $firstKey = $key; + $firstValues = $value; + break; + } + unset($dimensions[$key]); + + $results = array(); + foreach ($firstValues as $firstValue) { + foreach (self::product($dimensions, $template) as $result) { + $result[$firstKey] = $firstValue; + $results[] = $result; + } + } + + return $results; + } } diff --git a/tests/phpunit/CRM/Utils/ArrayTest.php b/tests/phpunit/CRM/Utils/ArrayTest.php index 8b2509f2fa..2d31965abc 100644 --- a/tests/phpunit/CRM/Utils/ArrayTest.php +++ b/tests/phpunit/CRM/Utils/ArrayTest.php @@ -33,7 +33,7 @@ class CRM_Utils_ArrayTest extends CiviUnitTestCase { $inputs[] = array( 'lang' => 'en', 'msgid' => 'greeting', - 'familiar' => false, + 'familiar' => FALSE, 'value' => 'Hello' ); $inputs[] = array( @@ -54,7 +54,7 @@ class CRM_Utils_ArrayTest extends CiviUnitTestCase { $inputs[] = array( 'lang' => 'en', 'msgid' => 'greeting', - 'familiar' => true, + 'familiar' => TRUE, 'value' => 'Hey' ); @@ -82,4 +82,41 @@ class CRM_Utils_ArrayTest extends CiviUnitTestCase { $this->assertEquals($expected, CRM_Utils_Array::collect('catWord', $arr)); } + function testProduct0() { + $actual = CRM_Utils_Array::product( + array(), + array('base data' => 1) + ); + $this->assertEquals(array( + array('base data' => 1), + ), $actual); + } + + function testProduct1() { + $actual = CRM_Utils_Array::product( + array('dim1' => array('a', 'b')), + array('base data' => 1) + ); + $this->assertEquals(array( + array('base data' => 1, 'dim1' => 'a'), + array('base data' => 1, 'dim1' => 'b'), + ), $actual); + } + + function testProduct3() { + $actual = CRM_Utils_Array::product( + array('dim1' => array('a', 'b'), 'dim2' => array('alpha', 'beta'), 'dim3' => array('one', 'two')), + array('base data' => 1) + ); + $this->assertEquals(array( + array('base data' => 1, 'dim1' => 'a', 'dim2' => 'alpha', 'dim3' => 'one'), + array('base data' => 1, 'dim1' => 'a', 'dim2' => 'alpha', 'dim3' => 'two'), + array('base data' => 1, 'dim1' => 'a', 'dim2' => 'beta', 'dim3' => 'one'), + array('base data' => 1, 'dim1' => 'a', 'dim2' => 'beta', 'dim3' => 'two'), + array('base data' => 1, 'dim1' => 'b', 'dim2' => 'alpha', 'dim3' => 'one'), + array('base data' => 1, 'dim1' => 'b', 'dim2' => 'alpha', 'dim3' => 'two'), + array('base data' => 1, 'dim1' => 'b', 'dim2' => 'beta', 'dim3' => 'one'), + array('base data' => 1, 'dim1' => 'b', 'dim2' => 'beta', 'dim3' => 'two'), + ), $actual); + } } -- GitLab