Append key/value pair to hash with << in Ruby

192,613

Solution 1

There is merge!.

h = {}
h.merge!(key: "bar")
# => {:key=>"bar"}

Solution 2

Since hashes aren't inherently ordered, there isn't a notion of appending. Ruby hashes since 1.9 maintain insertion order, however. Here are the ways to add new key/value pairs.

The simplest solution is

h[:key] = "bar"

If you want a method, use store:

h.store(:key, "bar")

If you really, really want to use a "shovel" operator (<<), it is actually appending to the value of the hash as an array, and you must specify the key:

h[:key] << "bar"

The above only works when the key exists. To append a new key, you have to initialize the hash with a default value, which you can do like this:

h = Hash.new {|h, k| h[k] = ''}
h[:key] << "bar"

You may be tempted to monkey patch Hash to include a shovel operator that works in the way you've written:

class Hash
  def <<(k,v)
    self.store(k,v)
  end
end

However, this doesn't inherit the "syntactic sugar" applied to the shovel operator in other contexts:

h << :key, "bar" #doesn't work
h.<< :key, "bar" #works

Solution 3

No, I don't think you can append key/value pairs. The only thing closest that I am aware of is using the store method:

h = {}
h.store("key", "value")

Solution 4

Similar as they are, merge! and store treat existing hashes differently depending on keynames, and will therefore affect your preference. Other than that from a syntax standpoint, merge!'s key: "value" syntax closely matches up against JavaScript and Python. I've always hated comma-separating key-value pairs, personally.

hash = {}
hash.merge!(key: "value")
hash.merge!(:key => "value")
puts hash

{:key=>"value"}

hash = {}
hash.store(:key, "value")
hash.store("key", "value")
puts hash

{:key=>"value", "key"=>"value"}

To get the shovel operator << working, I would advise using Mark Thomas's answer.

Solution 5

Perhaps you want Hash#merge ?

1.9.3p194 :015 > h={}
 => {} 
1.9.3p194 :016 > h.merge(:key => 'bar')
 => {:key=>"bar"} 
1.9.3p194 :017 > 

If you want to change the array in place use merge!

1.9.3p194 :016 > h.merge!(:key => 'bar')
 => {:key=>"bar"} 
Share:
192,613
jcarpio
Author by

jcarpio

Updated on July 08, 2022

Comments

  • jcarpio
    jcarpio almost 2 years

    In Ruby, one can append values to existing arrays using <<:

    a = []
    a << "foo"
    

    but, can you also append key/value pairs to an existing hash?

    h = {}
    h << :key "bar"
    

    I know you can do:

    h[:key] = ""
    h[:key] << "bar"
    

    but that's not I want.

    Thanks.

  • PericlesTheo
    PericlesTheo over 10 years
    I don't think merge! qualifies as simply creating a new key/value pair as it's actually used for another purpose.
  • Michael Durrant
    Michael Durrant over 10 years
    Good point. I think it may also end up depending on other factors that the OP didn't post.
  • PericlesTheo
    PericlesTheo over 10 years
    I don't think merge it's a good idea because it returns a new array; not adding the new pair to the existing hash.
  • Michael Durrant
    Michael Durrant over 10 years
    How about the merge! (i.e. to existing array) instead of merge ?
  • PericlesTheo
    PericlesTheo over 10 years
    merge! technically works in this context but I think it should be used for merging two hashes rather than simply adding a new pair. Also, if you benchmark merge!, it's slower than store ;)
  • jcarpio
    jcarpio over 10 years
    Ah, thanks. Looks like the store method does something similar. This was mostly an academic question as I've seen << referred as a way to append to arrays but wanted to see if it worked for hashes. Thanks again.
  • PericlesTheo
    PericlesTheo over 10 years
    The difference is this: store simply adds a new pair, it doesn't care if the key already exists. with merge! however, entries with duplicate keys are overridden so the method does a bit more than just adding pairs. If you benchmark the two, you will find store to be faster(trivial however especially on small hashes)
  • sawa
    sawa over 10 years
    @jcarpio Thanks. Hash#merge! returns the receiver hash just like Array#<< returns the receiver array. Hash#store is a completely different thing. Also, with the hash syntax sugar key: "bar" in the argument position, I thought this was the closest you can get to your notation. I knew this one is closer to what you wanted.
  • jcarpio
    jcarpio over 10 years
    Thorough! Thanks for the contribution and insight.
  • Will
    Will almost 10 years
    Thank you so much! I was having a problem adding multiple values. I was trying hash[:a] = 1 hash[:b] = 2 puts hash[:a] output 1,2 The merge! fixed this issue, now hash[:a] output 1
  • steel
    steel about 9 years
    To note, as of Ruby 1.9, Hashes are ordered.
  • Mark Thomas
    Mark Thomas about 9 years
    Ruby 1.9+ maintains insertion order, but a Hash as a data structure doesn't have the concept of any kind of inherent order.