我有一个嵌套的哈希,我需要添加更深层次的嵌套属性/值对。
示例A:
a = {}
a['x']['y']['z'] << 8
通常我必须这样做:
示例B:
a = {}
a['x'] ||= a['x'] = {}
a['x']['y'] ||= a['x']['y'] = {}
a['x']['y']['z'] ||= a['x']['y']['z'] = []
否则,我会得到undefined method '<<' for nil:NillClass
。
代码A中是否有某种类型的速记或函数而不是代码B?
答案 0 :(得分:5)
任何深度的深层嵌套哈希的最优雅解决方案是:
hash = Hash.new { |h, k| h[k] = h.dup.clear }
或者,甚至更好(归功于@Stefan)
hash = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
这样可以访问任何级别:
hash[:a1][:a2][:a3][:a4] = :foo
#⇒ {:a1=>{:a2=>{:a3=>{:a4=>:foo}}}}
想法是克隆哈希本身内的default_proc
。
答案 1 :(得分:1)
为了给一个背景,有一个方法Hash#dig
,自红宝石2.3以来就存在。有了这个,您可以安全地尝试读取任意数量的键:
{a: {b: {c: 1}}}.dig :a, :b, :c # => 1
{}.dig :a, :b, :c # => nil
当然,这并不能解决您的问题。您正在寻找写版本。这是Hash#bury
形式的proposed but rejected in Ruby core。
此方法几乎完全符合您的要求,但它只能设置嵌套的哈希值而不是附加到嵌套数组:
# start with empty hash
hash = {}
# define the inner array
hash.bury :x, :y, :z, []
# add to the inner array
hash[:x][:y][:z] << :some_val
您可以通过ruby-bury gem获取此方法,也可以从their source code
获取实施方法答案 2 :(得分:1)
以下是可以采取的几种方法。
arr = [:a1, :a2, :a3, :a4, :foo]
使用Enumerable#reduce(又名inject
)
def hashify(arr)
arr[0..-3].reverse_each.reduce(arr[-2]=>arr[-1]) { |h,x| { x=>h } }
end
hashify(arr)
#=> {:a1=>{:a2=>{:a3=>{:a4=>:foo}}}}
使用递归
def hashify(arr)
first, *rest = arr
rest.size == 1 ? { first=>rest.first } : { first=>hashify(rest) }
end
hashify(arr)
#=> {:a1=>{:a2=>{:a3=>{:a4=>:foo}}}}
答案 3 :(得分:0)