ソースを参照

Implemented Collection::combine()

Jose Lorenzo Rodriguez 12 年 前
コミット
4de65e7dc2

+ 74 - 0
src/Collection/CollectionTrait.php

@@ -628,6 +628,80 @@ trait CollectionTrait {
 	}
 
 /**
+ * Returrns a new collection where the values extracted based on a value path
+ * and then indexed by a key path. Optionally this method can produce parent
+ * groups absed on a group property path.
+ *
+ * ### Examples:
+ *
+ * {{{
+ * $items = [
+ *	['id' => 1, 'name' => 'foo', 'parent' => 'a'],
+ *	['id' => 2, 'name' => 'bar', 'parent' => 'b'],
+ *	['id' => 3, 'name' => 'baz', 'parent' => 'a'],
+ * ];
+ *
+ * $combined = (new Collection($items))->combine('id', 'name');
+ *
+ * //Result will look like this when converted to array
+ * [
+ *	1 => 'foo',
+ *	2 => 'bar',
+ *	3 => 'baz,
+ * ];
+ *
+ * $combined = (new Collection($items))->combine('id', 'name', 'parent');
+ *
+ * //Result will look like this when converted to array
+ * [
+ *	'a' => [1 => 'foo', 3 => 'baz'],
+ *	'b' => [2 => 'bar']
+ * ];
+ * }}}
+ *
+ * @param callable|string $keyPath the column name path to use for indexing
+ * or a function returning the indexing key out of the provided element
+ * @param callable|string $valuePath the column name path to use as the array value
+ * or a function returning the value out of the provided element
+ * @param callable|string $valuePath the column name path to use as the parent
+ * grouping key or a function returning the key out of the provided element
+ * @return \Cake\Collection\Collection
+ */
+	public function combine($keyPath, $valuePath, $groupPath = null) {
+		$options = [
+			'keyPath' => $this->_propertyExtractor($keyPath),
+			'valuePath' => $this->_propertyExtractor($valuePath),
+			'groupPath' => $groupPath ? $this->_propertyExtractor($groupPath) : null
+		];
+
+		$mapper = function($value, $key, $mapReduce) use ($options) {
+			$rowKey = $options['keyPath'];
+			$rowVal = $options['valuePath'];
+
+			if (!($options['groupPath'])) {
+				$mapReduce->emit($rowVal($value, $key), $rowKey($value, $key));
+				return;
+			}
+
+			$key = $options['groupPath']($value, $key);
+			$mapReduce->emitIntermediate(
+				[$rowKey($value, $key) => $rowVal($value, $key)],
+				$key
+			);
+		};
+
+		$reducer = function($values, $key, $mapReduce) {
+			$result = [];
+			foreach ($values as $value) {
+				$result += $value;
+			}
+			$mapReduce->emit($result, $key);
+		};
+
+		return new Collection(new MapReduce($this, $mapper, $reducer));
+	}
+
+/**
  * Returns an array representation of the results
  *
  * @param boolean $preserveKeys whether to use the keys returned by this

+ 45 - 0
tests/TestCase/Collection/CollectionTest.php

@@ -631,4 +631,49 @@ class CollectionTest extends TestCase {
 		$this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
 	}
 
+/**
+ * Tests the combine method
+ *
+ * @return void
+ */
+	public function testCombine() {
+		$items = [
+			['id' => 1, 'name' => 'foo', 'parent' => 'a'],
+			['id' => 2, 'name' => 'bar', 'parent' => 'b'],
+			['id' => 3, 'name' => 'baz', 'parent' => 'a']
+		];
+		$collection = (new Collection($items))->combine('id', 'name');
+		$expected = [1 => 'foo', 2 => 'bar', 3 => 'baz'];
+		$this->assertEquals($expected, $collection->toArray());
+
+		$expected = ['foo' => 1, 'bar' => 2, 'baz' => 3];
+		$collection = (new Collection($items))->combine('name', 'id');
+		$this->assertEquals($expected, $collection->toArray());
+
+		$collection = (new Collection($items))->combine('id', 'name', 'parent');
+		$expected = ['a' => [1 => 'foo', 3 => 'baz'], 'b' => [2 => 'bar']];
+		$this->assertEquals($expected, $collection->toArray());
+
+		$expected = [
+			'0-1' => ['foo-0-1' => '0-1-foo'],
+			'1-2' => ['bar-1-2' => '1-2-bar'],
+			'2-3' => ['baz-2-3' => '2-3-baz']
+		];
+		$collection = (new Collection($items))->combine(
+			function($value, $key) {
+				return $value['name'] . '-' . $key;
+			},
+			function($value, $key) {
+				return $key . '-' . $value['name'];
+			},
+			function($value, $key) {
+				return $key . '-' . $value['id'];
+			}
+		);
+		$this->assertEquals($expected, $collection->toArray());
+
+		$collection = (new Collection($items))->combine('id', 'crazy');
+		$this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
+	}
+
 }