Ruby: Как мне удалить значения nil / empty из вложенного хэша и почему мои попытки этого не делают? - PullRequest
1 голос
/ 05 июня 2019

Я пытаюсь написать метод, который удаляет все ключи во вложенном хэше, которые указывают на ноль рекурсивно.

Например:

{:a=>nil, :b=>"b", :c=>nil, :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}

становится:

{:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}

Мне не очень повезло.

Моя последняя попытка выглядит так:

def deep_compact(hash)
  hash.reject do |key, value|
    deep_compact(value) if value.class == Hash
    next true if value.nil? || value.empty?
  end
end

Здесь я хочу перебрать каждую пару значений ключа в хэше.Если значение является хешем, я хочу сделать то же самое для этого хеша.Я хочу отклонить пару, если значение равно нулю или пусто.В противном случае я хочу сохранить его.

Результат не тот, который я хочу:

#=> {:b=>"b", :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}

Я также пытался:

def deep_compact(hash)
  hash.compact.transform_values do |value|
    deep_compact(value) if value.class == Hash
    value
  end
end

Опять получаютот же результат:

#=> {:b=>"b", :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}

Это заставляет меня поверить, что либо я что-то упустил, либо мое понимание рекурсии неверно.

Близки ли мои попытки?Что мне нужно сделать, чтобы получить желаемый результат: {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}?

Ответы [ 3 ]

1 голос
/ 05 июня 2019

Я обнаружил, что, разместив мой рекурсивный вызов функции в конце блока, я получил большую часть пути туда. (Это хвостовая рекурсия?)

Я также вызываю reject для хэша, возвращенного transform_values, чтобы удалить все пустые пары.

Это достигает того, что я хотел:

def deep_compact(hash)
  hash.compact.transform_values do |value|
    next value unless value.class == Hash
    deep_compact(value)
  end.reject { |_k, v| v.empty? }
end
> h
=> {:a=>nil, :b=>"b", :c=>nil, :d=>{:dd=>"dd", :ee=>nil, :ff=>"ff"}, :e=>{:gg=>nil, :hh=>nil}}
> deep_compact h
=> {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
1 голос
/ 05 июня 2019

Другая опция, использующая Hash # reject! , меняет исходный хэш:

def deep_compact(h)
  h.each { |_, v| deep_compact(v) if v.is_a? Hash }.reject! { |_, v| v.nil? || v.empty? }
end

deep_compact(h)
#=> {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
1 голос
/ 05 июня 2019

Хитрость заключается в том, чтобы рекурсивно сжимать вложенные хэши, а затем удалять пустые значения.

compact = ->(hash) { 
  hash.is_a?(Hash) ?
    hash.map { |k, v| [k, compact.(v)] }.
         to_h.
         delete_if { |_, v| v.nil? || v.respond_to?(:empty?) && v.empty? } :
    hash 
}
compact.(input)
#⇒ {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...