diff --git a/README.md b/README.md index fd894d2..048a8a7 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,22 @@ $doubles = $collection->map(function ($value) { }); ``` +### Map Into +The `mapInto()` method allows you to take any collection and in one call create +a new collection of a given type, run a callable across the data, and have it +immediately collected into the new collection. + +Here's an example where we have created new collections that are restricted to +allowing only integers to be collected in the first, and strings in the second: +```php +$ints = new IntegerCollection([1, 2, 3, 4, 5]); +$strings = $ints->mapInto(function (int $item): string { + return (string)($item*10); +}, StringCollection::class); +``` +We now have a `StringCollection` which contains a string representation of +every value from the original `$ints` collection, multiplied by 10. + ### Each Sometimes we need to iterate over values, but we don't want to make any changes. That is where `each()` is different to `map()`. Otherwise, the diff --git a/src/Collection.php b/src/Collection.php index 58a0951..a91555e 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -146,21 +146,45 @@ private function checkType($value, ?string $type) /** * Copy entries into a new collection * - * @param string $collection The collection type to copy into + * @param string $type The collection type to copy into * @return self * @throws TypeError */ - public function into(string $collection): Collection + public function into(string $type): Collection { - if (!class_exists($collection)) { - throw new TypeError(sprintf('Unknown class name "%s"', $collection)); + return self::newCollectionOfType($type, $this->getArrayCopy()); + } + + /** + * Map collection into a new collection of a given type + * + * @param callable $callable + * @param string $type + * @return Collection + */ + public function mapInto(callable $callable, string $type): self + { + return self::newCollectionOfType($type, array_map($callable, $this->getArrayCopy())); + } + + /** + * Get a new collection of a given type + * + * @param string $type The collection type that you want an instance of + * @param array $items The items that you want to collect immediately (defaults to nothing) + * @return self + */ + public static function newCollectionOfType(string $type, $items = []): Collection + { + if (!class_exists($type)) { + throw new TypeError(sprintf('Unknown class name "%s"', $type)); } - if (!is_subclass_of($collection, Collection::class) && - Collection::class !== $collection + if (!is_subclass_of($type, Collection::class) && + Collection::class !== $type ) { - throw new TypeError(sprintf('Class "%s" is not a Collection type', $collection)); + throw new TypeError(sprintf('Class "%s" is not a Collection type', $type)); } - return new $collection($this->getArrayCopy()); + return new $type($items); } /** diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 21c4ac2..03c383c 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -538,6 +538,46 @@ public function failToCollectIntoANonExitingType() $collection->into('my arbitrary type'); } + /** + * @test + */ + public function mapInto() + { + $stringCollection = new class extends Collection + { + protected $valueType = 'string'; + }; + + $intCollection = new class([1,2,3,4,5]) extends Collection + { + protected $valueType = 'integer'; + }; + + $finalCollection = $intCollection->mapInto(function (int $int): string { + return (string) ($int * 10); + }, get_class($stringCollection)); + + $finalCollection->each(function ($value, $key) use ($intCollection) { + $this->assertSame((string) ($intCollection[$key] * 10), $value); + }); + } + + /** + * @test + * @expectedException \TypeError + */ + public function mapIntoThrows() + { + $intCollection = new class([1,2,3,4,5]) extends Collection + { + protected $valueType = 'integer'; + }; + + $intCollection->mapInto(function (int $int): string { + return (string) ($int * 10); + }, get_class($intCollection)); + } + /** * @dataProvider collectibles * @param $data