0

I want to have two cases the same effect in ruby Hash, one is to add pair with the string, the other is to add pair with the string's symbol. This map is used to be as a map to construct ActiveRecord Model. But after trying, the result is unexpected.

map = {}
map['list_price'] = 14.56
puts "#{map.inspect}"
map[:list_price] =  23.45
puts "#{map.inspect}"

the result is:

{"list_price"=>14.56}
{:list_price=>23.45, "list_price"=>14.56}

is there a way to make two keys internally the same one key?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
ywenbo
  • 3,051
  • 6
  • 31
  • 46

1 Answers1

4

I think you have several possibilities there:

Monkey-patching

Change []= method of Hash to translate all string keys to symbols, while assigning a value to a key. Aside from other bad things related to MP, there is another drawback, as other methods can be used to add new hash elements (merge etc.), which you would have to implement too, if you would like to ensure that no strings are used.

Inheritance

You can inherit Hash class and override []= method, but the drawback from above still applies.

Wrapping

You can wrap a hash with your class, and implement only certain methods. That would impair usage, as other Hash methods will not be available to you.

Read Jorg Mittag's post linked above, not directly an answer to your question, but great as a reference for different methods of changing the behaviour of existing classes.

Generally, this is how your []= would look like in the first case, others being similar:

class Hash
  alias_method :old_set, :[]=

  def []= key, val
    key = key.to_sym if key.is_a? String
    old_set key, val
  end

  alias_method :old_get, :[]

  def [] key
    return old_get(key) if self.has_key?(key)
    return old_get(key.to_sym) if key.is_a? String
    old_get key
  end
end

map = {}
map['list_price'] = 14.56
puts map.inspect #=>{:list_price=>14.56}
puts map['list_price'] #=> 14.56
puts map[:list_price]  #=> 14.56
 
map[:list_price] = 23.45 
puts map.inspect #=>{:list_price=>23.45}
puts map['list_price'] #=> 23.45
puts map[:list_price]  #=> 23.45
Community
  • 1
  • 1
Mladen Jablanović
  • 43,461
  • 10
  • 90
  • 113
  • yes, but sometimes i want store string key as symbols, sometimes i want the string key to keep as it is. so i can not do as above. Finally i did conversion from string to symbols when i need to store as symbol keys, other cases go on. Anyway, thank you, you're right for the question instead of for my case, maybe i don't expressed clearly. – ywenbo Dec 18 '10 at 08:30