初始化和更新深度嵌套哈希的更好方法
Better way to initialize and update deeply nested Hash
我有一个 Hash
,我想在更深的层次上向其中插入一些数据,但在任何层次上都可能缺少一个键。所以,我有条件地初始化它,然后在每个级别更新它的值。
什么是更好的编写方法或可以使代码不那么丑陋的方法?
data[:foo] ||= {}
data[:foo][:bar] ||= {}
data[:foo][:bar][:baz] ||= []
data[:foo][:bar][:baz] << 99
使用哈希自动生成:
data = Hash.new { |h, k| h[k] = h.dup.clear }
#⇒ {}
# or, credits to @Amadan:
data = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
#⇒ {}
data[:foo][:bar][:baz] = 42
data
#⇒ {:foo=>{:bar=>{:baz=>42}}}
这里使用的技巧是我们使用 Hash#default_proc
创建嵌套键。
针对您的情况:
(data[:foo][:bar][:baz] = []) << 99
你也可以这样做:
class SpecialHash < Hash
def [](key)
if has_key?(key)
super(key)
else
self[key] = self.class.new
end
end
end
h = SpecialHash.new
h[:foo][:bar][:baz] = "Baz"
h # => {:foo=>{:bar=>{:baz=>"Baz"}}}
鸭子类型与Hash
完全相同。
您可以将相同的代码重新格式化为:
class SpecialHash < Hash
def [](key)
return super if has_key?(key)
self[key] = self.class.new
end
end
甚至
class SpecialHash < Hash
def [](key)
has_key?(key) ? super : self[key] = self.class.new
end
end
可以使用递归。
def stuff_it(h, first_key, *rest_keys, val)
if rest_keys.empty?
(h[first_key] ||= []) << val
else
h[first_key] = stuff_it(h[first_key] ||= {}, *rest_keys, val)
end
h
end
stuff_it({ a: 1 }, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:bar=>{:baz=>[99]}}}
stuff_it({ a: 1, foo: { b: 2 } }, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:baz=>[99]}}}
stuff_it({ a: 1, foo: { b: 2, bar: { c: 3 } } }, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[99]}}}
h = { a: 1, foo: { b: 2, bar: { c: 3, baz: [88] } } }
stuff_it(h, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
h # => {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
从上一个例子可以看出,该方法是破坏性的。稍作改动即可使其无损。
def stuff_it(g, first_key, *rest_keys, val)
h = g.merge(g)
if rest_keys.empty?
h[first_key] = h[first_key] ? h[first_key].dup << val : [val]
else
h[first_key] = stuff_it(h[first_key] ||= {}, *rest_keys, val)
end
h
end
h = { a: 1, foo: { b: 2, bar: { c: 3 } } }
stuff_it(h, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[99]}}}
h #=> { a: 1, foo: { b: 2, bar: { c: 3 } } }
h = { a: 1, foo: { b: 2, bar: { c: 3, baz: [88] } } }
stuff_it(h, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
h #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88]}}}
我有一个 Hash
,我想在更深的层次上向其中插入一些数据,但在任何层次上都可能缺少一个键。所以,我有条件地初始化它,然后在每个级别更新它的值。
什么是更好的编写方法或可以使代码不那么丑陋的方法?
data[:foo] ||= {}
data[:foo][:bar] ||= {}
data[:foo][:bar][:baz] ||= []
data[:foo][:bar][:baz] << 99
使用哈希自动生成:
data = Hash.new { |h, k| h[k] = h.dup.clear }
#⇒ {}
# or, credits to @Amadan:
data = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
#⇒ {}
data[:foo][:bar][:baz] = 42
data
#⇒ {:foo=>{:bar=>{:baz=>42}}}
这里使用的技巧是我们使用 Hash#default_proc
创建嵌套键。
针对您的情况:
(data[:foo][:bar][:baz] = []) << 99
你也可以这样做:
class SpecialHash < Hash
def [](key)
if has_key?(key)
super(key)
else
self[key] = self.class.new
end
end
end
h = SpecialHash.new
h[:foo][:bar][:baz] = "Baz"
h # => {:foo=>{:bar=>{:baz=>"Baz"}}}
鸭子类型与Hash
完全相同。
您可以将相同的代码重新格式化为:
class SpecialHash < Hash
def [](key)
return super if has_key?(key)
self[key] = self.class.new
end
end
甚至
class SpecialHash < Hash
def [](key)
has_key?(key) ? super : self[key] = self.class.new
end
end
可以使用递归。
def stuff_it(h, first_key, *rest_keys, val)
if rest_keys.empty?
(h[first_key] ||= []) << val
else
h[first_key] = stuff_it(h[first_key] ||= {}, *rest_keys, val)
end
h
end
stuff_it({ a: 1 }, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:bar=>{:baz=>[99]}}}
stuff_it({ a: 1, foo: { b: 2 } }, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:baz=>[99]}}}
stuff_it({ a: 1, foo: { b: 2, bar: { c: 3 } } }, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[99]}}}
h = { a: 1, foo: { b: 2, bar: { c: 3, baz: [88] } } }
stuff_it(h, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
h # => {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
从上一个例子可以看出,该方法是破坏性的。稍作改动即可使其无损。
def stuff_it(g, first_key, *rest_keys, val)
h = g.merge(g)
if rest_keys.empty?
h[first_key] = h[first_key] ? h[first_key].dup << val : [val]
else
h[first_key] = stuff_it(h[first_key] ||= {}, *rest_keys, val)
end
h
end
h = { a: 1, foo: { b: 2, bar: { c: 3 } } }
stuff_it(h, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[99]}}}
h #=> { a: 1, foo: { b: 2, bar: { c: 3 } } }
h = { a: 1, foo: { b: 2, bar: { c: 3, baz: [88] } } }
stuff_it(h, :foo, :bar, :baz, 99)
#=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
h #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88]}}}