From 55eefa45fc8436a338d125ddf9ba0d1aa9e04b45 Mon Sep 17 00:00:00 2001 From: Philip Ross Date: Sat, 22 Jul 2023 06:19:38 -0700 Subject: [PATCH] Add yield `key` in `Hash#transform_values` and `value` in `#transform_keys` (#13608) --- spec/std/hash_spec.cr | 21 +++++++++++++++++++++ src/hash.cr | 23 +++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/spec/std/hash_spec.cr b/spec/std/hash_spec.cr index 85a3ad2833f8..66f9c7a9adee 100644 --- a/spec/std/hash_spec.cr +++ b/spec/std/hash_spec.cr @@ -882,6 +882,13 @@ describe "Hash" do h2.should be_empty end + it "transforms keys with values included" do + h1 = {1 => "a", 2 => "b", 3 => "c"} + + h2 = h1.transform_keys { |k, v| "#{k}#{v}" } + h2.should eq({"1a" => "a", "2b" => "b", "3c" => "c"}) + end + it "transforms values" do h1 = {"a" => 1, "b" => 2, "c" => 3} @@ -905,6 +912,13 @@ describe "Hash" do h2.should be_empty end + it "transforms values with keys included" do + h1 = {"a" => 1, "b" => 2, "c" => 3} + + h2 = h1.transform_values { |v, k| "#{k}#{v}" } + h2.should eq({"a" => "a1", "b" => "b2", "c" => "c3"}) + end + it "transform values in place" do h = {"a" => 1, "b" => 2, "c" => 3} @@ -912,6 +926,13 @@ describe "Hash" do h.should eq({"a" => 2, "b" => 3, "c" => 4}) end + it "transform values in place with keys included" do + h = {"a" => "1", "b" => "2", "c" => "3"} + + h.transform_values! { |v, k| "#{k}#{v}" } + h.should eq({"a" => "a1", "b" => "b2", "c" => "c3"}) + end + it "zips" do ary1 = [1, 2, 3] ary2 = ['a', 'b', 'c'] diff --git a/src/hash.cr b/src/hash.cr index 10a1eb4db23b..05ea0ca3b1de 100644 --- a/src/hash.cr +++ b/src/hash.cr @@ -1706,42 +1706,49 @@ class Hash(K, V) # Returns a new hash with all keys converted using the block operation. # The block can change a type of keys. + # The block yields the key and value. # # ``` # hash = {:a => 1, :b => 2, :c => 3} - # hash.transform_keys { |key| key.to_s } # => {"a" => 1, "b" => 2, "c" => 3} + # hash.transform_keys { |key| key.to_s } # => {"a" => 1, "b" => 2, "c" => 3} + # hash.transform_keys { |key, value| key.to_s * value } # => {"a" => 1, "bb" => 2, "ccc" => 3} # ``` - def transform_keys(& : K -> K2) : Hash(K2, V) forall K2 + def transform_keys(& : K, V -> K2) : Hash(K2, V) forall K2 each_with_object({} of K2 => V) do |(key, value), memo| - memo[yield(key)] = value + memo[yield(key, value)] = value end end # Returns a new hash with the results of running block once for every value. # The block can change a type of values. + # The block yields the value and key. # # ``` # hash = {:a => 1, :b => 2, :c => 3} - # hash.transform_values { |value| value + 1 } # => {:a => 2, :b => 3, :c => 4} + # hash.transform_values { |value| value + 1 } # => {:a => 2, :b => 3, :c => 4} + # hash.transform_values { |value, key| "#{key}#{value}" } # => {:a => "a1", :b => "b2", :c => "c3"} # ``` - def transform_values(& : V -> V2) : Hash(K, V2) forall V2 + def transform_values(& : V, K -> V2) : Hash(K, V2) forall V2 each_with_object({} of K => V2) do |(key, value), memo| - memo[key] = yield(value) + memo[key] = yield(value, key) end end # Destructively transforms all values using a block. Same as transform_values but modifies in place. # The block cannot change a type of values. + # The block yields the value and key. # # ``` # hash = {:a => 1, :b => 2, :c => 3} # hash.transform_values! { |value| value + 1 } # hash # => {:a => 2, :b => 3, :c => 4} + # hash.transform_values! { |value, key| value + key.to_s[0].ord } + # hash # => {:a => 99, :b => 101, :c => 103} # ``` # See `#update` for updating a *single* value. - def transform_values!(& : V -> V) : self + def transform_values!(& : V, K -> V) : self each_entry_with_index do |entry, i| - new_value = yield entry.value + new_value = yield entry.value, entry.key set_entry(i, Entry(K, V).new(entry.hash, entry.key, new_value)) end self