简体   繁体   English

Ruby:如何从嵌套哈希中删除nil / empty值,为什么我的尝试不这样做呢?

[英]Ruby: How do I remove nil/empty values from a nested hash and why aren't my attempts doing so?

I'm trying to write a method that removes all keys in a nested hash that point to nil recursively. 我正在尝试编写一个方法来删除嵌套哈希中递归指向nil的所有键。

For example: 例如:

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

becomes: 变为:

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

I'm not having much luck though. 我虽然运气不好。

My most recent attempt looks like: 我最近的尝试看起来像:

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

Here I want to iterate over each key value pair in the hash. 在这里,我想迭代哈希中的每个键值对。 If the value is a hash, I want to do the same for that hash. 如果值是哈希值,我想对该哈希值执行相同操作。 I want to reject the pair if the value is nil or empty. 如果值为零或空,我想拒绝该对。 Otherwise, I want to keep it. 否则,我想保留它。

The result isn't what I want: 结果不是我想要的:

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

I have also tried: 我也尝试过:

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

Again, I get the same result: 再次,我得到相同的结果:

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

This leaves me to believe that either I've missed something or my understanding of recursion is wrong. 这让我相信我错过了一些东西或者我对递归的理解是错误的。

Are any of my attempts close? 我的任何尝试都关闭了吗? What do I need to do to ensure I get the result I want: {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}} ? 我需要做些什么才能确保得到我想要的结果: {:b=>"b", :d=>{:dd=>"dd", :ff=>"ff"}}

The trick would be to recursively compact nested hashes and then to eliminate empty values. 诀窍是递归压缩嵌套哈希,然后消除空值。

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"}}

I discovered that by placing my recursive function call at the end of the block got me most of the way there. 我发现通过在块的末尾放置我的递归函数调用让我大部分都在那里。 (Is this 'tail-end' recursion?) (这是'尾端'递归吗?)

I also call reject on the hash returned by transform_values to removed any empty pairs. 我还对transform_values返回的哈希调用reject来删除任何空对。

This achieves what I wanted: 这实现了我想要的:

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"}}

Other option using Hash#reject! 其他选项使用Hash#reject! , it changes the original Hash: ,它改变了原来的哈希:

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"}}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM