【问题标题】:Ruby hash path to each leaf每个叶子的 Ruby 哈希路径
【发布时间】:2021-09-02 03:36:15
【问题描述】:

首先,如果这个问题已经存在,请见谅,我在这里深入寻找解决方案,但我已经找到了,但我觉得这是一个如此普遍的问题,找不到任何东西似乎很奇怪这里...

我的斗争如下:给定一个哈希,我需要将所有 PATHS 作为字符串数组返回到每个叶子;所以,例如:

  • {:a=> 1}['a']
  • {:a=>{:b=>3, :c=>4} 返回一个包含 两个 结果的数组:["a.b", "a.c"]
  • {:a=>[1, {:b=>2}]} 将导致 ["a.0", "a.1.b"] 等等……

我只找到了部分解决方案,并且有几十个代码行。像这样

def pathify
  self.keys.inject([]) do |acc, element|
    return acc if element.blank?

    if !(element.is_a?(Hash) || element.is_a?(Array))
      if acc.last.is_a?(Array)
        acc[acc.size-1] = acc.last.join('.')
      else
        acc << element.to_s
      end
    end

    if element.is_a?(Hash)
      element.keys.each do |key|
        if acc.last.is_a?(Array)
          acc.last << key.to_s
        else
          acc << [key.to_s]
        end
        element[key].pathify
      end
    end

    if element.is_a?(Array)
      acc << element.map(&:pathify)
    end
    acc
  end
end

但它并非在所有情况下都有效,而且效率极低。总结:有没有办法“路径化”一个哈希,以字符串数组的形式返回每个叶子的所有路径? 感谢您的帮助!

已编辑 添加一些规格

for {} it returns []
for {:a=>1} it returns ["a"]
for {:a=>1, :b=>1} it returns ["a", "b"]
for {:a=>{:b=>1}} it returns ["a.b"] (FAILED - 1) got: ["a"]
for {:a=>{:b=>1, :c=>2}} it returns ["a.b", "a.c"] (FAILED - 2) got: ["a"]
for {:a=>[1]} it returns ["a.0"] (FAILED - 3) got: ["a"]
for {:a=>[1, "b"]} it returns ["a.0", "a.1"] (FAILED - 4) got: ["a"]

【问题讨论】:

  • 它不适用于哪种情况,您认为它效率低下的基准是什么?我的怀疑是,事实上,你的抱怨只是上面的代码看起来“复杂”,你想要一个更短的解决方案。
  • 好点。让我添加一些规格以在失败时显示
  • 另外:您希望如何区分{'a' =&gt; { '1' =&gt; 'b'}}{'a' =&gt; ['b']}?这两个键都将由a.1 表示。如果您希望哈希键是符号而不是字符串,或者这种情况根本不会发生,那么这可能不是问题。
  • 也是个好问题。让我们假设键永远不会是整数。只有字符串/符号,所以 1 将始终是数组的第二个元素。没有日期,也没有特殊字符。只是[a-Z-_]

标签: ruby-on-rails ruby hash tree


【解决方案1】:
def show(key, path)
  if path.is_a? Array
    path.map {|p| "#{key}.#{p}"}
  else
    path == "" ? key.to_s : "#{key}.#{path}"
  end
end

def pathify(input)
  if input.is_a? Hash
    input.map do |k,v|
      sub_path = pathify(v)
      show(k, sub_path)
    end.flatten
  elsif input.is_a? Array
    input.map.with_index do |v, i|
      sub_path = pathify(v)
      show(i, sub_path)
    end.flatten
  else
    ""
  end
end

【讨论】:

    【解决方案2】:
    def leaf_paths(enum)
      return unless [Hash, Array].include? enum.class
    
      [].tap do |result|
        if enum.is_a?(Hash)
          enum.each { |k, v| result = attach_leaf_paths(k, v, result) }
        elsif enum.is_a?(Array)
          enum.each_with_index { |elem, index| result = attach_leaf_paths(index, elem, result) }
        end
      end
    end
    
    def attach_leaf_paths(key, value, result)
      if (children = leaf_paths(value))
        children.each { |child| result << "#{key}.#{child}" }
      else
        result << key.to_s
      end
      result
    end
    

    【讨论】:

      【解决方案3】:

      这与https://github.com/wteuber/yaml_normalizer/blob/b85dca7357df00757c471acb5dadb79a53dd27c1/lib/yaml_normalizer/ext/namespaced.rb非常相似

      所以我稍微调整了代码以满足您的需求:

      module Leafs
        def leafs(namespace = [], tree = {})
          each do |key, value|
            child_ns = namespace.dup << key
            if value.instance_of?(Hash)
              value.extend(Leafs).leafs child_ns, tree
            elsif value.instance_of?(Array)
              value.each.with_index.inject({}) {|h, (v,k)| h[k]=v; h}.extend(Leafs).leafs child_ns, tree
            else
              tree[child_ns.join('.')] = value
            end
          end
          tree.keys.to_a
        end
      end
      

      这里是如何使用它:

      h = {a: [1, "b"], c: {d:1}}
      h.extend(Leafs)
      h.leafs
      # => ["a.0", "a.1", "c.d"]
      

      希望对您有所帮助。

      【讨论】:

        【解决方案4】:
        def pathify(what)
            paths = []
            if what.is_a?(Array)
                what.each_with_index do | element, index |
                    paths+= pathify(element).map{|e| index.to_s + '.' + e.to_s}
                end
            elsif what.is_a?(Hash)
                what.each do |k,v|
                    paths+= pathify(v).map{|e| k.to_s + '.' + e.to_s}
                end
            else
                paths.append('')
            end
            paths.map{|e| e.delete_suffix('.')}
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-26
          • 1970-01-01
          • 1970-01-01
          • 2021-02-13
          • 2021-06-05
          • 2011-12-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多