[英]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]
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.