Skip to content

Commit

Permalink
Add yield key in Hash#transform_values and value in `#transform…
Browse files Browse the repository at this point in the history
…_keys` (#13608)
  • Loading branch information
baseballlover723 authored Jul 22, 2023
1 parent 9801815 commit 55eefa4
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
21 changes: 21 additions & 0 deletions spec/std/hash_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand All @@ -905,13 +912,27 @@ 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}

h.transform_values!(&.+(1))
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']
Expand Down
23 changes: 15 additions & 8 deletions src/hash.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 55eefa4

Please sign in to comment.