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

时间:2019-07-11 18:19:43

标签: ruby-on-rails ruby

我想获得技巧和示例,说明如何转换这样的不同结构:

x <- 2
x * prod(c(1.1, 1.2, 1.11, 1.21))
# [1] 3.545784 

像这样进入数组:

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

此数组的每个元素都是完整路径。

2 个答案:

答案 0 :(得分:3)

这是非常标准的Tree traversal problem。您可以通过递归使用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

它输出:

[: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

答案 1 :(得分:1)

代码

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

示例

对于问题中给出的哈希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]] 

说明

通过递归方法执行的操作总是很难解释。以我的经验,最好的方法是使用puts语句添加代码。但是,这本身是不够的,因为在查看输出时,很难跟踪获得特定结果的递归级别,方法何时调用自身以及返回的自身版本。解决方案是缩进和缩进结果,这是我在下面所做的。

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

# rem结尾的行(“删除”)是我添加到方法中的行。

doit(h)

导致显示以下内容。

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]]