Rails 6 adds Hash#deep_transform_values and Hash#deep_transform_values!


Transforming values in hash

Many times we come across situations where we require to perform a transformation on all the values of a hash.

Ruby 2.4 introduced Hash#transform_values and its destructive counterpart Hash#transform_values! to transform hash values. These methods return a new hash with all values converted by a block operation. One of the issues with this is that they only work on top level values:

2.5.6 :001 > hash = {a: 1, b: 2}.transform_values {|e| e.to_s}
=> {:a=>"1", :b=>"2"}
2.5.6 :002 > {a: 1, b: {c: 2}}.transform_values {|e| e.to_s}
=> {:a=>"1", :b=>"{:c=>2}"}
2.5.6 :003 > {a: 1, b: [1, 3]}.transform_values {|e| e.to_s}
=> {:a=>"1", :b=>"[1, 3]"}

As you can see, in the second & third example, when we have nested object with hash and arrays, it doesn’t work the way we would expect it to.

Rails 6 adds methods for deep transforming values

In our applications, we many times need to deal with more complex hashes, that can have other nested hashes or arrays.

To achieve this, Rails 6 has added Hash#deep_transform_values that returns a new hash as a result, and its destructive counterpart Hash#deep_transform_values! that updates the hash in place.

These methods are advanced versions of the Hash#transform_values and Hash#transform_values!, as they can handle nested or complex hashes.

Here is an example of these methods in action:

# Example of non destructive `deep_transform_values` returns new object after transformation 
2.6.3 :001 > hash = {personal: {first_name: "naren", last_name: "rajput"}, professional: {company: "saeloun"}, interests: ["reading", "coding"]}
=> {:personal => {:first_name => "naren", :last_name => "rajput"}, :professional => {:company => "saeloun"}, :interests => ["reading", "coding"]}
2.6.3 :002 > hash.deep_transform_values {|attribute| attribute.capitalize}
=> {:personal => {:first_name => "Naren", :last_name => "Rajput"}, :professional => {:company => "Saeloun"}, :interests => ["Reading", "Coding"]}
2.6.3 :003 > hash
=> {:personal => {:first_name => "naren", :last_name => "rajput"}, :professional => {:company => "saeloun"}, :interests => ["reading", "coding"]}
# Example of destructive `deep_transform_values!` updates the hash in place
2.6.3 :005 > hash = {personal: {first_name: "naren", last_name: "rajput"}, professional: {company: "saeloun"}, interests: ["reading", "coding"]}
=> {:personal => {:first_name => "naren", :last_name => "rajput"}, :professional => {:company => "saeloun"}, :interests => ["reading", "coding"]}
2.6.3 :006 > hash.deep_transform_values! {|attribute| attribute.capitalize}
=> {:personal => {:first_name => "Naren", :last_name => "Rajput"}, :professional => {:company => "Saeloun"}, :interests => ["Reading", "Coding"]}
2.6.3 :007 > hash
=> {:personal => {:first_name => "Naren", :last_name => "Rajput"}, :professional => {:company => "Saeloun"}, :interests => ["Reading", "Coding"]}