简体   繁体   English

将Hash深度转换为平坦的路径数组

[英]Deep transforming Hash into the flat Array of paths

I would like to get tips and examples of how can convert different structures like this: 我想获得技巧和示例,说明如何转换这样的不同结构:

h = {
    friend: [:id, :name],
    meta: {
        board: [:id, :name],
        column: [:id, :name, users: [:id, :name]]
    },
    trello: [:id, :name]
}

into the array like this: 像这样进入数组:

[[:friend, :id],
 [:friend, :name],
 [:meta, :board, :id],
 [:meta, :board, :name],
 [:meta, :column, :id],
 [:meta, :column, :name],
 [:meta, :column, :users, :id],
 [:meta, :column, :users, :name],
 [:trello, :id],
 [:trello, :name]]

Each element of this array is a full path. 该数组的每个元素都是完整路径。

It is pretty standard Tree traversal problem . 这是相当标准的Tree遍历问题 You could use DFS via recursion: 您可以通过递归使用DFS

# for Array.wrap; It's needed in pure ruby script, not in Rails
require 'active_support/all'

def deep_flatten(tree, path, result)
  tree.each do |key, value|
    Array.wrap(value).each do |e|
      if e.is_a? Hash
        deep_flatten(e, path + [key], result)
      else
        result << path + [key, e]
      end
    end
  end
end

tree = {
  friend: [:id, :name],
  meta: {
    board: [:id, :name],
    column: [:id, :name, users: [:id, :name]]
  },
  trello: [:id, :name]
}

result = []
deep_flatten(tree, [], result)
result.each do |line|
  puts line.inspect
end

It outputs: 它输出:

[:friend, :id]
[:friend, :name]
[:meta, :board, :id]
[:meta, :board, :name]
[:meta, :column, :id]
[:meta, :column, :name]
[:meta, :column, :users, :id]
[:meta, :column, :users, :name]
[:trello, :id]
[:trello, :name]

Array.wrap Array.wrap

Code

def doit(obj)
  case obj
  when Hash
    obj.each_with_object([]) do |(k,v),a|
      case v
      when Symbol
        a << v
      else
        doit(v).each { |aa| a << [k, *aa] }
      end
    end
  else
    obj.each_with_object([]) do |v,a|
      case v
      when Symbol
        a << v
      else
        doit(v).each { |aa| a << aa }
      end
    end
  end
end

Example

For the hash h given in the question the results are as follows. 对于问题中给出的哈希h ,结果如下。

doit(h)
  #=> [[:friend, :id], [:friend, :name],
  #    [:meta, :board, :id], [:meta, :board, :name], [:meta, :column, :id],
  #    [:meta, :column, :name], [:meta, :column, :users, :id],
  #    [:meta, :column, :users, :name],
  #    [:trello, :id], [:trello, :name]] 

Explanation 说明

The operations performed by recursive methods are always difficult to explain. 递归方法执行的操作始终很难解释。 In my experience the best way is to salt the code with puts statements. 以我的经验,最好的方法是用puts语句添加代码。 However, that in itself is not enough because when viewing output it is difficult to keep track of the level of recursion at which particular results are obtained, when the method calls itself and which version of itself to which it returns. 但是,这本身是不够的,因为在查看输出时,很难跟踪获得特定结果的递归级别,方法何时调用自身以及返回的自身版本。 The solution to that is to indent and un-indent results, which is what I've done below. 解决方案是缩进和缩进结果,这是我在下面所做的。

INDENT = 4

@col = -INDENT

def indent
  @col += INDENT
end

def undent
  @col -= INDENT
end

def pu(s)
  print " "*@col
  puts s
end

def doit(obj)
  begin                                                  # rem
    indent                                               # rem
    pu "passed obj = #{obj}"                             # rem   
    case obj
    when Hash
      pu "processing hash..."                            # rem
      obj.each_with_object([]) do |(k,v),a|
        pu "k=#{k}, v=#{v}, a=#{a}"
        case v
        when Symbol
          a << v
        else
          doit(v).each { |aa| a << [k, *aa] }
        end
      end
    else

      pu "processing array..."                           # rem
      obj.each_with_object([]) do |v,a|
        pu "v = #{v}"                                    # rem
        pu "a = #{a}"                                    # rem
        case v
        when Symbol
          pu "v is a symbol"                             # rem
          a << v
        else
          pu "calling doit(v). v is a hash or an array"  # rem
          doit(v).each { |aa| a << aa }
        end
      end
    end.
    tap { |o| pu "returning #{o}" }                      # rem
  ensure                                                 # rem
    undent                                               # rem
  end
end

The lines ending with # rem (for "remove") are the lines I've added to the method. # rem结尾的行(表示“删除”)是我添加到方法中的行。

doit(h)

causes the following to be displayed. 导致显示以下内容。

passed obj = {:friend=>[:id, :name], :meta=>{:board=>[:id, :name],
              :column=>[:id, :name, {:users=>[:id, :name]}]}, :trello=>[:id, :name]}
processing hash...
k=friend, v=[:id, :name], a=[]
    passed obj = [:id, :name]
    processing array...
    v = id
    a = []
    v is a symbol
    v = name
    a = [:id]
    v is a symbol
    returning [:id, :name]

k=meta, v={:board=>[:id, :name], :column=>[:id, :name, {:users=>[:id, :name]}]},
a=[[:friend, :id], [:friend, :name]]
    passed obj = {:board=>[:id, :name],
                  :column=>[:id, :name, {:users=>[:id, :name]}]}
    processing hash...
    k=board, v=[:id, :name], a=[]
        passed obj = [:id, :name]
        processing array...
        v = id
        a = []
        v is a symbol
        v = name
        a = [:id]
        v is a symbol
       returning [:id, :name]

    k=column, v=[:id, :name, {:users=>[:id, :name]}],
    a=[[:board, :id], [:board, :name]]
        passed obj = [:id, :name, {:users=>[:id, :name]}]
        processing array...
        v = id
        a = []
        v is a symbol
        v = name
        a = [:id]
        v is a symbol
        v = {:users=>[:id, :name]}
        a = [:id, :name]
        calling doit(v). v is a hash or an array
            passed obj = {:users=>[:id, :name]}
            processing hash...
            k=users, v=[:id, :name], a=[]
                passed obj = [:id, :name]
                processing array...
                v = id
                a = []
                v is a symbol
                v = name
                a = [:id]
                v is a symbol
                returning [:id, :name]
            returning [[:users, :id], [:users, :name]]
        returning [:id, :name, [:users, :id], [:users, :name]]
    returning [[:board, :id], [:board, :name], [:column, :id], [:column, :name],     
               [:column, :users, :id], [:column, :users, :name]]

k=trello, v=[:id, :name], a=[[:friend, :id], [:friend, :name], [:meta, :board, :id],
 [:meta, :board, :name], [:meta, :column, :id], [:meta, :column, :name],
 [:meta, :column, :users, :id], [:meta, :column, :users, :name]]
    passed obj = [:id, :name]
    processing array...
    v = id
    a = []
    v is a symbol
    v = name
    a = [:id]
    v is a symbol
    returning [:id, :name]
returning [[:friend, :id], [:friend, :name], [:meta, :board, :id],
           [:meta, :board, :name], [:meta, :column, :id], [:meta, :column, :name],
           [:meta, :column, :users, :id], [:meta, :column, :users, :name],
           [:trello, :id], [:trello, :name]]

 #=> [[:friend, :id], [:friend, :name], [:meta, :board, :id], [:meta, :board, :name],
      [:meta, :column, :id], [:meta, :column, :name], [:meta, :column, :users, :id],
      [:meta, :column, :users, :name], [:trello, :id], [:trello, :name]] 

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

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