0

I have a hash

  hash = {"some_wierd_name"=>"cheesemonster", .....}

and I want this hash as

  hash = {"preferred_name"=>"cheesemonster", ......}

What is the shortest way to do that?

Blair Anderson
  • 19,463
  • 8
  • 77
  • 114

4 Answers4

6
hash["preferred_name"] = hash.delete("some_wierd_name")

Hash keys are frozen strings, they can’t be modified inplace, and the frozen state can’t be removed from an object. That said, tricks with prepend and replace won’t work resulting in:

RuntimeError: can't modify frozen String

Therefore, there is the only possibility: to remove the old value and insert the new one.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
3
hash = {"some_wierd_name"=>"cheesemonster"}

hash["preferred_name"] = hash["some_wierd_name"]
hash.delete("some_wierd_name")
steenslag
  • 79,051
  • 16
  • 138
  • 171
  • Just a note that (since `hash.delete("some_wierd_name") => "cheesemonster"`) if this is packaged in a method, `hash` is needed as the last line. – Cary Swoveland Jul 05 '16 at 21:38
1
  • If we are looking to replace the key/value both, can be done easily by using rails except method. You can also use the delete method but one key/value pair at a time but be using except can remove 2 or more key/value pair.

    hash = {a: 1, b:2, c: 3}
    hash.except!(:a)[:d] = 4 
    and it is similar to these two following line
    hash.except!(:a) 
    hash[:d] = 4
    hash = {:b=>2, :c=>3, :d=>4}
    
  • Changing only key of the hash, value remains same. One can also use the reject. reject and delete_if are same.

    hash[:e] = hash.delete(:d) 
         or 
    temp = hash[d]
    hash.delete_if{|key| key ==:d }
    hash[:e] = temp
    hash = {:b=>2, :c=>3, :e=>4}
    
  • Changing only value, the key remains same. This one pretty easy.

    hash[:e] = 5
    

References :

Mukesh Kumar Gupta
  • 1,567
  • 20
  • 15
0

for a single key, use delete

hash["preferred_name"] = hash.delete("some_wierd_name")

If you need to update all the keys, i suggest using inject

new_hash = hash.inject({}) do |returned_hash, (key, value)|
  returned_hash[key] = value.upcase;
  returned_hash
end
Blair Anderson
  • 19,463
  • 8
  • 77
  • 114
  • 1
    Self-answers are not a problem and are not frowned upon in general. Also, the answer is correct and pretty complete, nothing to worry about. – Holger Just Jul 01 '16 at 16:51
  • As a tiny addition, instead of `inject`, you can also use [`each_with_object`](http://ruby-doc.org/core/Enumerator.html#method-i-each_with_object) to get rid of the second line in the block. – Holger Just Jul 01 '16 at 16:54
  • @HolgerJust, I would agree with you if it were a challenging question, but this one is not. I have seen this technique for renaming a key many, many times on SO. – Cary Swoveland Jul 01 '16 at 16:54
  • @HolgerJust “to get rid of the second line in the block”—`each_with_object` is **not about getting rid of last line**, it’s about mutating the same object instead of producing new one on each iteration. – Aleksei Matiushkin Jul 01 '16 at 16:59
  • 2
    Why `value.upcase`? And why not use `each_with_object({})` when you're not really injecting? Or use `h.merge(k => v.upcase)` as the block if you are injecting. And your `inject` version isn't renaming any keys, it is modifying the values. – mu is too short Jul 01 '16 at 16:59
  • @mudasobwa In this case, it is. With the `inject` idiom used here, there is already the same object returned each time. When using `each_with _object` (or even `each.with_object)`, it just becomes more explicit and more concise to write. – Holger Just Jul 01 '16 at 17:20
  • @HolgerJust I have no problems with self answers, but the timestamps between the answer and question were less than a minute apart, which leads me to believe that the question that the answer wasn't reasoned about from comments here, but just a shameless point-grab. – Sean Jul 01 '16 at 17:48
  • I also agree `each_with_object` is the optimal approach here `new_hash = hash.each_with_object({}) {|(k, v), hash| hash[k] = v.upcase }` – Sean Jul 01 '16 at 17:50
  • 1
    @Sean, I think "shameless point-grab" is rather harsh. Some very experienced Rubiests here regularly post questions and then immediately post an answer. In effect, they are saying, "This the best I could come up with. Is there a better way?" Typically it is a challenging problem they've encountered on the job. It has nothing to do with rep. – Cary Swoveland Jul 05 '16 at 06:56
  • @CarySwoveland you're exactly right, and i also like to ask/answer questions when a similar search returns articles/answers that are not answering the exact question in title. – Blair Anderson Jul 05 '16 at 20:07
  • @Sean I originally found this question http://stackoverflow.com/questions/6210572/how-to-replace-a-hash-key-with-another-key which looks to be more about regex than ruby hash particulars. /shrug i'm fine with downvotes :) not here for the points. – Blair Anderson Jul 05 '16 at 20:09
  • Answering your own question is also a way to get around the requirement that questions with working code are not suitable for SO (and should be moved to Code Review). :-) – Cary Swoveland Jul 05 '16 at 21:31