如何在 ruby​​ 的散列中制作 deep_slice

问题描述

我一直在寻找一种干净的方法来做到这一点,我找到了一些解决方法,但没有找到像切片这样的东西(有些人建议使用 gem,但我认为此操作不需要,如果我我错了),所以我发现自己有一个包含一堆散列的散列,我想要一种方法来对这个散列执行 Slice 操作,并从嵌套散列中获取键/值对,所以问题是:

ruby 中是否有类似 deep_slice 的东西?

示例:

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}

提前谢谢! ?

解决方法

环顾四周后,我决定自己实现它,这就是我解决它的方法:

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

上面的代码是一个经典的DFS,它利用了哈希类提供的merge!

您可以测试上面的代码here

,
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,:z]
arr = recurse(hash,keys.to_set)
  #=> [[:b,45],[:f,12],[:b,21],[:z,90]]

请注意,hash 与问题中给出的示例哈希略有不同。我添加了第二个嵌套键 :b 来说明返回散列而不是键值对数组的问题。如果我们将 arr 转换为散列,则 [:b,45] 对将被丢弃:

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

但是,如果需要,可以写:

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

我将 keys 从数组转换为集合只是为了加快查找速度 (keys.include?(k))。

如果散列包含嵌套散列数组和嵌套散列,则可以使用稍微修改的方法。