Лучший способ инициализировать и обновить глубоко вложенный хэш - PullRequest
0 голосов
/ 22 мая 2018

У меня есть Hash, и я хочу вставить в него некоторые данные на глубоком уровне, но ключ может отсутствовать на любом уровне.Итак, я условно инициализирую его перед обновлением его значения на каждом уровне.

Какой лучший способ написать это или подход, который может сделать код менее уродливым?

data[:foo] ||= {}
data[:foo][:bar] ||= {}
data[:foo][:bar][:baz] ||= []
data[:foo][:bar][:baz] << 99

Ответы [ 3 ]

0 голосов
/ 22 мая 2018

Вы могли бы также сделать что-то вроде:

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
0 голосов
/ 22 мая 2018

Можно использовать рекурсию.

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]}}}
0 голосов
/ 22 мая 2018

Использование хеш-авививификация :

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...