简体   繁体   English

将元素推送到红宝石哈希中的数组上

[英]pushing elements onto an array in a ruby Hash

In Ruby, to create a hash of arrays and push elements onto those arrays, I've seen two idioms. 在Ruby中,为了创建数组哈希并将元素推送到这些数组上,我看到了两个成语。 I'd like to know which one people prefer, and why. 我想知道人们更喜欢哪一个,以及为什么。 (Disclosure: I have my own opinion, but I want to make sure I'm not missing something obvious.) (披露:我有自己的看法,但我想确保我没有遗漏一些明显的东西。)

Approach 1: use Hash's fancy initializer: 方法1:使用Hash的花式初始化程序:

ht = Hash.new {|h,k| h[k]=[]}
ht["cats"] << "Jellicle"
ht["cats"] << "Mr. Mistoffelees"

This approach creates an empty array when you access ht with a key that doesn't yet exist. 当您使用尚不存在的密钥访问ht时,此方法会创建一个空数组。

Approach 2: simple initializer, fancy accessor: 方法2:简单的初始化器,花哨的访问器:

ht = {}
(ht["cats"] ||= []) << "Jellicle"
(ht["cats"] ||= []) << "Mr. Mistoffelees"

Do people have an opinion on which one is better (or situations where one is preferred over the other)? 人们对哪一个更好(或者哪个优先于另一个的情况)有意见?

Sometimes the hash is initially filled with data and later on it is only used to retrieve data. 有时哈希最初用数据填充,稍后它只用于检索数据。 In those cases I prefer the first possibility, because the default proc can be "emptied" (in Ruby 1.9). 在那些情况下,我更喜欢第一种可能性,因为默认的proc可以“清空”(在Ruby 1.9中)。

ht = Hash.new {|h,k| h[k]=[]}
ht["cats"] << "Jellicle"
ht["cats"] << "Mr. Mistoffelees"
ht["dogs"]
p ht
#=> {"cats"=>["Jellicle", "Mr. Mistoffelees"], "dogs"=>[]}

ht.default_proc = proc{}
ht["parrots"] #nil
p ht
#=> {"cats"=>["Jellicle", "Mr. Mistoffelees"], "dogs"=>[]} No parrots!

If you know in advance the number and the name for each key, then you can use the first option. 如果您事先知道每个密钥的编号和名称,则可以使用第一个选项。 Or even a simpler one 甚至更简单

ht = { "cats" => [] }

Otherwise, if you don't want (need) to preinitialize the hash, the second option is a good choice. 否则,如果您不希望(需要)预先初始化散列,则第二个选项是一个不错的选择。

In the OP, I said I had my own opinion. 在OP,我说我有自己的看法。 Here it is. 这里是。

Although the "fancy initializer" approach is elegant, it can lead to some really unexpected behavior -- specifically generating keys when you don't expect it -- and there's no way to know this by looking at the hash table. 虽然“花哨的初始化程序”方法很优雅,但它可能会导致一些非常意外的行为 - 特别是在您不期望它时生成密钥 - 并且无法通过查看哈希表来了解这一点。

Consider the following: 考虑以下:

>> ht1 = Hash.new {|h,k| h[k]=[]}
>> ht2 = {}
>> ht1["cats"] << "Jellicle"
=> ["Jellicle"]
>> (ht2["cats"] ||= []) << "Jellicle"
=> ["Jellicle"]

so far so good -- ht1 and ht2 are identical. 到目前为止这么好 - ht1和ht2是完全相同的。 but: 但:

>> ht1["dogs"] ? "got dogs" : "no dogs"
=> "got dogs"
>> ht2["dogs"] ? "got dogs" : "no dogs"
=> "no dogs"

Note that simply accessing ht1[some_key] changes the state of the hash table , ie it create a new entry. 请注意, 只需访问ht1 [some_key]即可更改哈希表的状态 ,即创建新条目。 You might argue that the end user should always use has_key?() to test for the presence of a hash entry -- and you'd be right -- but the above usage is an accepted idiom. 你可能会争辩说最终用户应该总是使用has_key?()来测试是否存在哈希条目 - 你是对的 - 但上面的用法是一个公认的习惯用法。 For the hash table to automagically create an entry would be an unexpected side effect, so you should be careful if the hash table is ever exposed to the end user. 对于自动创建条目的哈希表,这将是一个意想不到的副作用,因此如果哈希表暴露给最终用户,您应该小心。 (Note, however, that steenslag's answer shows how you can turn this off.) (请注意,steenslag的答案显示了如何关闭它。)

Personally, I prefer: 就个人而言,我更喜欢:

ht = Hash.new {|h,k| h[k]=[]}
ht["cats"] << "Jellicle"
ht["cats"] << "Mr. Mistoffelees"

It's a matter of maintenance and self-documentation to me. 这是我的维护和自我记录的问题。 The hash initialization dirty-work occurs once. 哈希初始化脏工作发生一次。 From then on, all assignments to the arrays in the hash occurs in a standard manner, which will result in your code looking normal. 从那时起,哈希中对数组的所有赋值都以标准方式进行,这将导致代码看起来正常。

The alternate method: 替代方法:

ht = {}
(ht["cats"] ||= []) << "Jellicle"
(ht["cats"] ||= []) << "Mr. Mistoffelees"

starts off looking normal, but each assignment has what I call "visual noise". 开始看似正常,但每个任务都有我称之为“视觉噪音”。 We have to mentally decode what ||= [] means every time something is pushed onto an array in the hash. 我们必须在心理上解码每次将某些东西推入散列中的数组时||= []含义。 It's mentally fatiguing, and if you're doing it throughout the code, would lead to less elegant looking code and a constant reassessment of what the heck the code is doing at THAT spot. 这是精神疲劳,如果你在代码这样做,将导致更少的优雅寻找代码和究竟发生了什么的代码是在那个地方做一个持续的重新评估。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM