From 327fd2f2082eee5f012c74f410c41b5b20208b5c Mon Sep 17 00:00:00 2001 From: Bartosz Schiller Date: Sat, 22 Dec 2018 16:56:35 +0100 Subject: [PATCH 1/3] Add static method to create new Stream and allow instant chaining --- README.md | 2 +- src/Stream.php | 11 +++++++++++ tests/unit/StreamTest.php | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c1b325..83296da 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ $isOdd = function($num) { }; // Create our stream. -$stream = new phpstreams\Stream(fibonacci()); +$stream = new phpstreams\Stream(fibonacci()); // or phpstreams\Stream::of(fibonacci()); // Finally, use these to create our result. $oddFibo = $stream->filter($isOdd) // Keep only the odd numbers diff --git a/src/Stream.php b/src/Stream.php index ebcca58..14d0c10 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -41,6 +41,17 @@ public function __construct($source) $this->source = $source; } + /** + * Create a new stream from a traversable source. + * + * @param Traversable|array $source The source to create the stream from. + * @throws InvalidStreamException if the given source is not usable as a stream. + */ + public static function of($source) + { + return new static($source); + } + /** * Check whether a source is valid. * diff --git a/tests/unit/StreamTest.php b/tests/unit/StreamTest.php index a3195bb..1c8ae9d 100644 --- a/tests/unit/StreamTest.php +++ b/tests/unit/StreamTest.php @@ -36,6 +36,13 @@ public function testConstructor() $this->assertInstanceOf("Traversable", $value); } + public function testStaticCreation() + { + $value = Stream::of([]); + + $this->assertInstanceOf("Traversable", $value); + } + /** * @test * @expectedException phpstreams\exception\InvalidStreamException From 6bbdcf31f0f43257f6f77ccbc1e9e5e35148cdb2 Mon Sep 17 00:00:00 2001 From: Bartosz Schiller Date: Sat, 22 Dec 2018 22:24:27 +0100 Subject: [PATCH 2/3] Add function returning first value from stream or default value --- src/Stream.php | 15 +++++++++++++++ tests/unit/StreamTest.php | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/Stream.php b/src/Stream.php index 14d0c10..7a84954 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -276,6 +276,21 @@ public function collect(Collector $collector) return $collector->get(); } + /** + * Get first element from stream + * + * @param $defaultValue [optional] Default value to return if stream is empty + * @return first element if available, default value otherwise + */ + public function first($defaultValue = null) + { + foreach ($this as $value) { + return $value; + } + + return $defaultValue; + } + /** * Flatten the underlying stream. * diff --git a/tests/unit/StreamTest.php b/tests/unit/StreamTest.php index 1c8ae9d..01e40ac 100644 --- a/tests/unit/StreamTest.php +++ b/tests/unit/StreamTest.php @@ -144,6 +144,30 @@ public function testCollect() $this->assertEquals(42, $instance->collect($collector)); } + public function testFirst() + { + $stream = new Stream([4, 5, 6, 7]); + $emptyStream = new Stream([]); + + $this->assertEquals(4, $stream->first()); + + $this->assertEquals(null, $emptyStream->first()); + + $this->assertEquals('empty', $emptyStream->first('empty')); + + $result = $stream->filter(function ($a) { + return $a > 10; + })->first(); + + $this->assertEquals(null, $result); + + $result = $stream->filter(function ($a) { + return $a > 5; + })->first(); + + $this->assertEquals(6, $result); + } + public function testIsSortedWithSortedSource() { $sortedSource = $this->getMockBuilder('phpstreams\Stream') From d529334b416ab68668c25711904b1ef39b3a78db Mon Sep 17 00:00:00 2001 From: Bartosz Schiller Date: Fri, 28 Dec 2018 22:04:41 +0100 Subject: [PATCH 3/3] Add MappingCollector for mapping keys and values in collect phase --- src/Collectors.php | 13 ++++++ src/collectors/MappingCollector.php | 61 +++++++++++++++++++++++++++++ tests/unit/CollectorsTest.php | 34 ++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/collectors/MappingCollector.php diff --git a/src/Collectors.php b/src/Collectors.php index 69af266..64dd488 100644 --- a/src/Collectors.php +++ b/src/Collectors.php @@ -4,6 +4,7 @@ use phpstreams\collectors\AveragingCollector; use phpstreams\collectors\ReducingCollector; +use phpstreams\collectors\MappingCollector; /** * Utility class containing various collectors. @@ -70,4 +71,16 @@ function ($current, $element) use (&$first, $delimiter) { return $current . $element; }); } + + /** + * Get a collector that maps keys and values. + * + * @param callable $keyMapper + * @param callable $valueMapper [optional] A value mapper. Defaults to pass-through mapper. + * @return Collector + */ + public static function mapping(callable $keyMapper, callable $valueMapper = null) + { + return new MappingCollector($keyMapper, $valueMapper); + } } diff --git a/src/collectors/MappingCollector.php b/src/collectors/MappingCollector.php new file mode 100644 index 0000000..c000fd4 --- /dev/null +++ b/src/collectors/MappingCollector.php @@ -0,0 +1,61 @@ + + */ +class MappingCollector implements Collector +{ + /** + * Mapped array + * + * @var array + */ + private $map = []; + + /** + * Callable for mapping key + * + * @var callable + */ + private $keyMapper; + + /** + * Callable for mapping value + * + * @var callable + */ + private $valueMapper; + + public function __construct(callable $keyMapper, callable $valueMapper = null) + { + $this->keyMapper = $keyMapper; + + if (!is_callable($valueMapper)) + { + $valueMapper = function ($k, $v) { + return $v; + }; + } + + $this->valueMapper = $valueMapper; + } + + public function add($key, $value) + { + $keyMapper = $this->keyMapper; + $valueMapper = $this->valueMapper; + + $this->map[$keyMapper($key, $value)] = $valueMapper($key, $value); + } + + public function get() + { + return $this->map; + } +} diff --git a/tests/unit/CollectorsTest.php b/tests/unit/CollectorsTest.php index 83e341d..f627d45 100644 --- a/tests/unit/CollectorsTest.php +++ b/tests/unit/CollectorsTest.php @@ -59,4 +59,38 @@ public function testReducing() $this->assertEquals(24, $instance->get()); } + + public function testMapping() + { + $instance = Collectors::mapping(function ($k, $v) { + return $k . "a"; + }, function($k, $v) { + return $v + 10; + }); + + $instance->add("a", 2); + $instance->add("b", 3); + $instance->add("c", 4); + + $this->assertEquals([ + "aa" => 12, + "ba" => 13, + "ca" => 14 + ], $instance->get()); + + + $instance = Collectors::mapping(function ($k, $v) { + return $k . "a"; + }); + + $instance->add("a", 2); + $instance->add("b", 3); + $instance->add("c", 4); + + $this->assertEquals([ + "aa" => 2, + "ba" => 3, + "ca" => 4 + ], $instance->get()); + } }