在Ruby中使用<<向哈希表添加键值对。

96

Ruby中,可以使用<<将值附加到现有数组中:

a = []
a << "foo"

但是,你也可以向现有哈希表中添加键值对吗?

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

我知道你能做到:

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

但那不是我想要的。

谢谢。


26
为什么不用 h[:key] = "bar" - Hauleth
7
这个语句无效:h[:key] << "bar" - Chris Nielsen
6个回答

161

有一个 merge! 方法。

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

4
我认为merge!不能简单地被视为创建一个新的键/值对,因为它实际上用于另一个目的。 - PericlesTheo
好观点。我认为这也可能取决于其他一些OP没有发布的因素。 - Michael Durrant
1
啊,谢谢。看起来store方法做了类似的事情。这主要是一个学术问题,因为我看到<<被用作向数组添加元素的一种方式,但想知道它是否适用于哈希表。再次感谢。 - jcarpio
5
区别在于:store 只是添加一个新的键值对,它不在意该键是否已经存在;而 merge! 则会覆盖重复键的条目,因此该方法做的事情比只是添加键值对要多一些。如果你对这两个方法进行基准测试,你会发现 store 更快(尤其是在小哈希表上)。 - PericlesTheo
2
@jcarpio 谢谢。Hash#merge! 返回接收器哈希,就像 Array#<< 返回接收器数组一样。Hash#store 是完全不同的东西。此外,使用哈希语法糖 key: "bar" 在参数位置,我认为这是最接近你想要的符号。我知道这个更接近你想要的。 - sawa
非常感谢!我遇到了一个添加多个值的问题。我尝试使用 hash[:a] = 1 hash[:b] = 2,但输出是 1,2。通过使用 merge! 方法解决了这个问题,现在 hash[:a] 的输出为 1。 - Will

106

由于哈希表本质上没有顺序,所以不存在追加的概念。然而,自Ruby 1.9以来,哈希表保持插入顺序。以下是添加新键值对的方法。

最简单的解决方案是

h[:key] = "bar"

如果您需要一种方法,请使用 store:
h.store(:key, "bar")

如果你真的非常想要使用“铲子”运算符 (<<),它实际上是将数组附加到哈希值上,并且你必须指定键:

h[:key] << "bar"

上述方法仅适用于键已存在的情况。如果要添加新的键,则必须使用默认值初始化哈希表,可以按照以下方式进行:

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

你可能会尝试对Hash进行猴子补丁,以包含一个像你写的那样工作的铲运算符:
class Hash
  def <<(k,v)
    self.store(k,v)
  end
end

然而,在其他情况下应用于铲子操作符的“语法糖”并未继承到此处:
h << :key, "bar" #doesn't work
h.<< :key, "bar" #works

非常透彻!感谢您的贡献和见解。 - jcarpio
需要注意的是,从Ruby 1.9开始,哈希表是有序的。 - steel
1
Ruby 1.9+保持插入顺序,但作为数据结构的哈希表并没有任何内在顺序的概念。 - Mark Thomas

30

不,我认为您无法追加键/值对。我所知道的唯一最接近的方法是使用store方法:

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

4
也许你想要使用 Hash#merge ?
1.9.3p194 :015 > h={}
 => {} 
1.9.3p194 :016 > h.merge(:key => 'bar')
 => {:key=>"bar"} 
1.9.3p194 :017 > 

如果你想原地修改数组,请使用merge!

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

我认为merge不是一个好主意,因为它返回一个新数组;而不是将新的键值对添加到现有哈希表中。 - PericlesTheo
尝试使用merge!(即合并到现有数组)代替merge,怎么样? - Michael Durrant
1
merge! 在这个语境中技术上是可以使用的,但我认为它应该用于合并两个哈希而不仅仅是添加一个新对。此外,如果你对 merge! 进行基准测试,它比 store 更慢 ;) - PericlesTheo

4
与它们相似,merge!store 根据键名不同而不同地处理已存在的哈希表,因此会影响您的偏好。除此之外,从语法角度来看,merge!key: "value" 语法与 JavaScript 和 Python 高度匹配。就个人而言,我一直讨厌用逗号分隔键值对。
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"}

若要使用铲子运算符 <<,我建议使用Mark Thomas的答案。


2

我曾经需要做类似的事情,但是我需要添加具有相同键的值。当我使用合并或更新时,我无法推送具有相同键的值。所以我不得不使用哈希数组。

    my_hash_static = {:header =>{:company => 'xx', :usercode => 'xx', :password => 'xx',
                      :type=> 'n:n', :msgheader => from}, :body=>[]}
    my_hash_dynamic = {:mp=>{:msg=>message, :no=>phones} }        
    my_hash_full = my_hash_static[:body].push my_hash_dynamic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接