Можно использовать рекурсию.
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]}}}