diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php index f1c64c7b0cd1..40cfdfdffd64 100644 --- a/src/Illuminate/Collections/Arr.php +++ b/src/Illuminate/Collections/Arr.php @@ -643,6 +643,35 @@ public static function mapWithKeys(array $array, callable $callback) return $result; } + /** + * Run an associative map over each of the item's values filtered by their key. + * + * The callback should return the same resultset with the modified values of the given keys. + * + * @param array $array + * @param string|array $keys + * @param callable $callback + * @return array + */ + public static function mapByKey(array $array, $keys, callable $callback) + { + $result = []; + + foreach ($array as $parentKey => $row) { + foreach ($row as $key => $value) { + if (! in_array($key, (array) $keys, true)) { + $result[$parentKey][$key] = $value; + + continue; + } + + $result[$parentKey][$key] = $callback($value, $key, $row); + } + } + + return $result; + } + /** * Run a map over each nested chunk of items. * diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 71b432cef89c..cdabd68e6c5d 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -843,6 +843,20 @@ public function mapWithKeys(callable $callback) return new static(Arr::mapWithKeys($this->items, $callback)); } + /** + * Run an associative map over each of the item's values filtered by their key. + * + * The callback should return the same resultset with the modified values of the given keys. + * + * @param string|array $keys + * @param callable $callback + * @return static + */ + public function mapByKey($keys, callable $callback) + { + return new static(Arr::mapByKey($this->items, $keys, $callback)); + } + /** * Merge the collection with the given items. * diff --git a/src/Illuminate/Collections/LazyCollection.php b/src/Illuminate/Collections/LazyCollection.php index f4e61b457e03..3e4af081370e 100644 --- a/src/Illuminate/Collections/LazyCollection.php +++ b/src/Illuminate/Collections/LazyCollection.php @@ -843,6 +843,20 @@ public function mapWithKeys(callable $callback) }); } + /** + * Run an associative map over each of the item's values filtered by their key. + * + * The callback should return the same resultset with the modified values of the given keys. + * + * @param string|array $keys + * @param callable $callback + * @return static + */ + public function mapByKey($keys, callable $callback) + { + return $this->passthru('mapByKey', func_get_args()); + } + /** * Merge the collection with the given items. * diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php index 82044805b7cb..53df66a18b40 100644 --- a/tests/Support/SupportArrTest.php +++ b/tests/Support/SupportArrTest.php @@ -6,6 +6,8 @@ use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; +use Illuminate\Support\Number; +use Illuminate\Support\Str; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use stdClass; @@ -804,6 +806,60 @@ public function testMapWithKeys() ); } + public function testMapByKey() + { + $invoices = [ + ['recipient' => 'Dries', 'currency' => 'USD', 'amount' => 1], + ['recipient' => 'Taylor', 'currency' => 'USD', 'amount' => 25.67], + ['recipient' => 'Nuno', 'currency' => 'EUR', 'amount' => 123987], + ]; + + $invoices = Arr::mapByKey($invoices, 'amount', function ($value, $key, $row) { + return Number::currency($value, $row['currency']); + }); + + $this->assertEquals( + [ + ['recipient' => 'Dries', 'currency' => 'USD', 'amount' => '$1.00'], + ['recipient' => 'Taylor', 'currency' => 'USD', 'amount' => '$25.67'], + ['recipient' => 'Nuno', 'currency' => 'EUR', 'amount' => '€123,987.00'], + ], + $invoices + ); + + $posts = [ + ['title' => 'Hello World', 'body' => '# Hello World', 'preview' => 'Test *post*'], + ['title' => 'New Release', 'body' => '**New Release**', 'preview' => '**Release notes**'], + ]; + + $posts = Arr::mapByKey($posts, ['body', 'preview'], fn ($value) => Str::markdown($value)); + + $this->assertEquals( + [ + ['title' => 'Hello World', 'body' => "

Hello World

\n", 'preview' => "

Test post

\n"], + ['title' => 'New Release', 'body' => "

New Release

\n", 'preview' => "

Release notes

\n"], + ], + $posts + ); + + $data = [ + ['foo' => 'Dries', 'bar' => 'Joe', 'baz' => 'Jess'], + ['foo' => 'Taylor', 'baz' => 'James'], + ['bar' => 'Nuno'], + ]; + + $data = Arr::mapByKey($data, ['bar', 'baz'], fn ($value) => Str::lower($value)); + + $this->assertEquals( + [ + ['foo' => 'Dries', 'bar' => 'joe', 'baz' => 'jess'], + ['foo' => 'Taylor', 'baz' => 'james'], + ['bar' => 'nuno'], + ], + $data + ); + } + public function testMapByReference() { $data = ['first' => 'taylor', 'last' => 'otwell']; diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index beaa1f84109d..b67551f46ad4 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -3053,6 +3053,27 @@ public function testMapWithKeysCallbackKey($collection) ); } + #[DataProvider('collectionClassProvider')] + public function testMapByKey($collection) + { + $data = new $collection([ + ['foo' => 'Dries', 'bar' => 'Joe', 'baz' => 'Jess'], + ['foo' => 'Taylor', 'baz' => 'James'], + ['bar' => 'Nuno'], + ]); + + $data = $data->mapByKey(['bar', 'baz'], fn ($value) => Str::lower($value)); + + $this->assertEquals( + [ + ['foo' => 'Dries', 'bar' => 'joe', 'baz' => 'jess'], + ['foo' => 'Taylor', 'baz' => 'james'], + ['bar' => 'nuno'], + ], + $data->all() + ); + } + #[DataProvider('collectionClassProvider')] public function testMapInto($collection) {