简体   繁体   English

如何在 ruby 上的 hash 中制作 deep_slice

[英]how to make a deep_slice in a hash on ruby

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

Is there something like deep_slice in ruby? ruby 中是否有类似 deep_slice 的东西?

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!提前谢谢!

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!上面的代码是一个经典的DFS ,它利用了合并! provided by the hash class.由 hash class 提供。

You can test the code above 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, :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.请注意, hash与问题中给出的示例 hash 略有不同。 I added a second nested key :b to illustrate the problem of returning a hash rather than an array of key-value pairs.我添加了第二个嵌套键:b来说明返回 hash 而不是键值对数组的问题。 Were we to convert arr to a hash the pair [:b, 45] would be discarded:如果我们将arr转换为 hash 对[:b, 45]将被丢弃:

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) ).我将keys从数组转换为集合只是为了加快查找速度( keys.include?(k) )。

A slightly modified approach could be used if the hash contained nested arrays of hashes as well as nested hashes.如果 hash 包含嵌套的散列 arrays 以及嵌套的散列,则可以使用稍微修改的方法。

My version我的版本

maybe it should help也许它应该有帮助


def deep_slice( obj, *args )
    deep_arg = {}
    slice_args = []
    args.each do |arg|
        if arg.is_a? Hash
            arg.each do |hash|
                key, value = hash
                if obj[key].is_a? Hash
                    deep_arg[key] = deep_slice( obj[key], *value ) 
                elsif obj[key].is_a? Array
                    deep_arg[key] = obj[key].map{ |arr_el| deep_slice( arr_el, *value) }
                end
            end
        elsif arg.is_a? Symbol
            slice_args << arg
        end
    end
    obj.slice(*slice_args).merge(deep_arg)
end

Object to slice Object 切片

obj = {
  "id": 135,
  "kind": "transfer",
  "customer": {
    "id": 1,
    "name": "Admin",
  },
  "array": [
        {
            "id": 123,
            "name": "TEST",
            "more_deep": {
                "prop": "first",
                "prop2": "second"
            }
        },
        {
            "id": 222,
            "name": "2222"
        }
    ]
}

Schema to slice要切片的架构

deep_slice(
    obj,
    :id,
    customer: [
        :name
    ],
    array: [
        :name,
        more_deep: [
            :prop2
        ]
    ]
)

Result结果

{
    :id=>135, 
    :customer=>{
        :name=>"Admin"
    }, 
    :array=>[
        {
         :name=>"TEST", 
         :more_deep=>{
            :prop2=>"second"
         }
        }, 
        {
            :name=>"2222"
        }
    ]
}

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

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