diff --git a/README.md b/README.md index e969be9..32415f3 100755 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ The following operations return a single value by consuming all of the sequence: * **toArray** - will return a PHP array with zero-based keys * **toMap** - will return a PHP associative array +* **keyBy** - will key the sequence by a given selector and return as a PHP associative array * **first** - will return the first element as an `util.data.Optional` instance. A value will be present if the sequence was not empty. * **single** - like `first()`, but raises an exception if more than one element is contained in the sequence. * **count** - will return the number of elements in the sequence diff --git a/src/main/php/util/data/Sequence.class.php b/src/main/php/util/data/Sequence.class.php index 26a2499..7d1ac11 100755 --- a/src/main/php/util/data/Sequence.class.php +++ b/src/main/php/util/data/Sequence.class.php @@ -15,6 +15,7 @@ * @test util.data.unittest.SequenceFilteringTest * @test util.data.unittest.SequenceFlatteningTest * @test util.data.unittest.SequenceIteratorTest + * @test util.data.unittest.SequenceKeyByTest * @test util.data.unittest.SequenceMappingTest * @test util.data.unittest.SequenceReductionTest * @test util.data.unittest.SequenceResultSetTest @@ -180,6 +181,39 @@ public function toMap($map= null) { return $return; } + /** + * Keys the results with one of the following: + * + * - `'key'`: Uses the elements' "key" field as keys, and the whole element as values. + * - `['key' => 'name']`: Uses the elements' ID as keys, and the 'name' field as values. + * - `fn($e) => yield $e['key'] => $e['name']`: Most flexible approach. + * + * @param string|[:string]|(function(var): iterable) $selector + * @return [:var] + */ + public function keyBy($selector) { + $return= []; + if (is_string($selector)) { + foreach ($this->elements as $element) { + $return[$element[$selector]]= $element; + } + } else if (is_array($selector)) { + $k= key($selector); + $v= $selector[$k]; + foreach ($this->elements as $element) { + $return[$element[$k]]= $element[$v]; + } + } else { + $mapper= Functions::$APPLY->newInstance($selector); + foreach ($this->elements as $element) { + foreach ($mapper($element) as $k => $v) { + $return[$k]= $v; + } + } + } + return $return; + } + /** * Counts all elements * diff --git a/src/test/php/util/data/unittest/SequenceKeyByTest.class.php b/src/test/php/util/data/unittest/SequenceKeyByTest.class.php new file mode 100755 index 0000000..7fc9dcb --- /dev/null +++ b/src/test/php/util/data/unittest/SequenceKeyByTest.class.php @@ -0,0 +1,45 @@ + 'one', 'user' => ['power' => 1000]]; + $two= ['_id' => 'two', 'user' => ['power' => 2000]]; + + Assert::equals(['one' => $one, 'two' => $two], Sequence::of([$one, $two])->keyBy('_id')); + } + + #[Test, Values([[['_id' => 'power'], ['one' => 1000, 'two' => 2000]], [['power' => '_id'], [1000 => 'one', 2000 => 'two']]])] + public function key_by_map($fields, $expected) { + $one= ['_id' => 'one', 'power' => 1000]; + $two= ['_id' => 'two', 'power' => 2000]; + + Assert::equals($expected, Sequence::of([$one, $two])->keyBy($fields)); + } + + #[Test] + public function key_by_callable() { + $one= ['_id' => 'one', 'user' => ['power' => 1000]]; + $two= ['_id' => 'two', 'user' => ['power' => 2000]]; + + Assert::equals( + ['one' => 1000, 'two' => 2000], + Sequence::of([$one, $two])->keyBy(function($r) { return [$r['_id'] => $r['user']['power']]; }) + ); + } + + #[Test] + public function key_by_generator() { + $one= ['_id' => 'one', 'user' => ['power' => 1000]]; + $two= ['_id' => 'two', 'user' => ['power' => 2000]]; + + Assert::equals( + ['one' => 1000, 'two' => 2000], + Sequence::of([$one, $two])->keyBy(function($r) { yield $r['_id'] => $r['user']['power']; }) + ); + } +} \ No newline at end of file