how to make a deep_slice in a hash on ruby

浪子不回头ぞ 提交于 2021-02-08 03:26:50

问题


I was looking around for a clean way to do this and I found some workarounds but did not find anything like the slice (some people recommended to use a gem but I think is not needed for this operations, pls correct me if I am wrong), so I found myself with a hash that contains a bunch of hashes and I wanted a way to perform the Slice operation over this hash and get also the key/value pairs from nested hashes, so the question:

Is there something like deep_slice in ruby?

Example:

input: a = {b: 45, c: {d: 55, e: { f: 12}}, g: {z: 90}}, keys = [:b, :f, :z]

expected output: {:b=>45, :f=>12, :z=>90}

Thx in advance! 👍


回答1:


After looking around for a while I decided to implement this myself, this is how I fix it:

a = {b: 45, c: {d: 55, e: { f: 12}}, g: {z: 90}}
keys = [:b, :f, :z]
def custom_deep_slice(a:, keys:)
    result = a.slice(*keys)
    a.keys.each do |k|
        if a[k].class == Hash
            result.merge! custom_deep_slice(a: a[k], keys: keys)
        end
    end
    result
end
c_deep_slice = custom_deep_slice(a: a, keys: keys)
p c_deep_slice

The code above is a classic DFS, which takes advantage of the merge! provided by the hash class.

You can test the code above here




回答2:


require 'set'

def recurse(h, keys)
  h.each_with_object([]) do |(k,v),arr|
    if keys.include?(k)
      arr << [k,v]
    elsif v.is_a?(Hash)
      arr.concat(recurse(v,keys))
    end
  end
end

hash = { b: 45, c: { d: 55, e: { f: 12 } }, g: { b: 21, z: 90 } }
keys = [:b, :f, :z]
arr = recurse(hash, keys.to_set)
  #=> [[:b, 45], [:f, 12], [:b, 21], [:z, 90]]

Notice that hash differs slightly from the example hash given in the question. I added a second nested key :b to illustrate the problem of returning a hash rather than an array of key-value pairs. Were we to convert arr to a hash the pair [:b, 45] would be discarded:

arr.to_h
  #=> {:b=>21, :f=>12, :z=>90}

If desired, however, one could write:

arr.each_with_object({}) { |(k,v),h| (h[k] ||= []) << v }
  #=> {:b=>[45, 21], :f=>[12], :z=>[90]}

I converted keys from an array to a set merely to speed lookups (keys.include?(k)).

A slightly modified approach could be used if the hash contained nested arrays of hashes as well as nested hashes.



来源:https://stackoverflow.com/questions/65756598/how-to-make-a-deep-slice-in-a-hash-on-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!