Why is 'transform_keys' method undefined?

Viewed 3119

This example is taken directly from the Ruby 2.4.1 documentation, and I can confirm I am running 2.4.1:

({a: 1, b: 2, c: 3}).transform_keys {|k| k.to_s}

When I execute it, I receive the following error:

NoMethodError: undefined method `transform_keys' for {:a=>1, :b=>2, :c=>3}:Hash

Why is the transform_keys method not defined?

3 Answers

As observed in another question, it appears that http://ruby-doc.org currently (erroneously) generates the documentation for Ruby 2.4.1 based on Ruby trunk instead of the actually released 2.4.1 version.

Unfortunately, the Hash#transform_keys method is not yet released as part of any 2.4 release. It was developed and comitted to Ruby trunk with Feature #13583 but was not (yet) backported to the stable 2.4 branch.

As a workaround for that, you can use this method instead:

def transform_keys(hash)
  result = {}
  hash.each_pair do |key, value|
    result[yield(key)] = value
  end
  result
end

Equivalently (that is: a bit shorter but also a slightly slower) you could use this:

def transform_keys(hash)
  hash.keys.each_with_object({}) do |key, result|
    result[yield(key)] = hash[key]
  end
end

If you are bold, you can add this as a core-patch to the Hash class where you then just have to replace every mention of hash with self.

Note that ActiveSupport (i.e. Rails) brings a core-patch with this exact method since about forever. They use a mixture of both implementations.

Thank you Holger, you inspired me.

This works for converting keys from string to symbol also for nested hashes, as I needed:

class Hash

  def keys_to_sym
    result = {}
    self.each_pair do |key, value|
       value = value.keys_to_sym if value.class == Hash
       result[key.to_sym] = value
    end
  result
  end

end

So you can transform this:

h = {
  "key1"=>"value1",
  "key2"=>"value2",
  "key3"=>{"key4"=>"value4", "key5"=>"value5"},
  "key6"=>{"key7"=>"value7", "key8"=>{"key9"=>"value9"}}
}

into this:

h_transformed = {
  :key1=>"value1",
  :key2=>"value2",
  :key3=>{:key4=>"value4", :key5=>"value5"},
  :key6=>{:key7=>"value7", :key8=>{:key9=>"value9"}}
}

Well, It seems like you are trying this code in ruby shell.(irb)

But it's available in Rails.

> (irb): ({a: 1, b: 2, c: 3}).transform_keys {|k| k.to_s} 
  NoMethodError: undefined method `transform_keys' for {:a=>1, :b=>2, :c=>3}:Hash    
  from (irb):1
  from /home/chitresh/.rvm/rubies/ruby-2.2.10/bin/irb:11:in `<main>'

Use rails console via command rails console or rails c

> ({a: 1, b: 2, c: 3}).transform_keys {|k| k.to_s}
 => {"a"=>1, "b"=>2, "c"=>3}
Related