Creating hashes of hashes in Ruby allows for convenient two (or more) dimensional lookups. However, when inserting one must always check if the first index already exists in the hash. For example:
在Ruby中创建散列可以方便地进行二维查询。但是,插入时必须始终检查散列中是否已经存在第一个索引。例如:
h = Hash.new
h['x'] = Hash.new if not h.key?('x')
h['x']['y'] = value_to_insert
It would be preferable to do the following where the new Hash is created automatically:
最好在新哈希自动创建的地方执行以下操作:
h = Hash.new
h['x']['y'] = value_to_insert
Similarly, when looking up a value where the first index doesn't already exist, it would be preferable if nil is returned rather than receiving an undefined method for '[]' error.
类似地,在查找第一个索引不存在的值时,如果返回nil而不是接收未定义的方法来处理'[]'错误,则更可取。
looked_up_value = h['w']['z']
One could create a Hash wrapper class that has this behavior, but is there an existing a Ruby idiom for accomplishing this task?
可以创建具有此行为的散列包装器类,但是否存在用于完成此任务的Ruby习惯用法?
2 个解决方案
#1
54
You can pass the Hash.new
function a block that is executed to yield a default value in case the queried value doesn't exist yet:
你可以传递散列。新函数一个块,该块被执行以产生一个默认值,以防查询的值还不存在:
h = Hash.new { |h, k| h[k] = Hash.new }
Of course, this can be done recursively.
当然,这可以递归地完成。
/EDIT: Wow, there's an article answering this very question.
/编辑:哇,有一篇文章回答了这个问题。
For the sake of completeness, here's the solution from the article for arbitrary depth hashes:
为了完整性起见,以下是本文中关于任意深度散列的解决方案:
hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))
Credits go to Kent from Data Noise.
从数据噪声中获得的积分属于Kent。
#2
4
Autovivification, as it's called, is both a blessing and a curse. The trouble can be that if you "look" at a value before it's defined, you're stuck with this empty hash in the slot and you would need to prune it off later.
所谓的“自活”,既是一种祝福,也是一种诅咒。问题可能在于,如果在定义一个值之前“查看”它,就会陷入槽中的这个空散列,之后需要对它进行修剪。
If you don't mind a bit of anarchy, you can always just jam in or-equals style declarations which will allow you to construct the expected structure as you query it:
如果您不介意出现一点混乱,那么您可以只使用or-equals样式声明,这将允许您在查询时构造预期的结构:
((h ||= { })['w'] ||= { })['z']
#1
54
You can pass the Hash.new
function a block that is executed to yield a default value in case the queried value doesn't exist yet:
你可以传递散列。新函数一个块,该块被执行以产生一个默认值,以防查询的值还不存在:
h = Hash.new { |h, k| h[k] = Hash.new }
Of course, this can be done recursively.
当然,这可以递归地完成。
/EDIT: Wow, there's an article answering this very question.
/编辑:哇,有一篇文章回答了这个问题。
For the sake of completeness, here's the solution from the article for arbitrary depth hashes:
为了完整性起见,以下是本文中关于任意深度散列的解决方案:
hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))
Credits go to Kent from Data Noise.
从数据噪声中获得的积分属于Kent。
#2
4
Autovivification, as it's called, is both a blessing and a curse. The trouble can be that if you "look" at a value before it's defined, you're stuck with this empty hash in the slot and you would need to prune it off later.
所谓的“自活”,既是一种祝福,也是一种诅咒。问题可能在于,如果在定义一个值之前“查看”它,就会陷入槽中的这个空散列,之后需要对它进行修剪。
If you don't mind a bit of anarchy, you can always just jam in or-equals style declarations which will allow you to construct the expected structure as you query it:
如果您不介意出现一点混乱,那么您可以只使用or-equals样式声明,这将允许您在查询时构造预期的结构:
((h ||= { })['w'] ||= { })['z']