递归!试试 Elixir :-)
为了跟踪递归方法,我写了很多 puts 并添加了一个级别号。
因为我没有 Rails,所以我删除了 Rails 的东西。通过这个轻微的修改,您的输入(其中书籍数组不是数组!)和您的代码:
schema =
{ "type"=>"object",
"properties"=>{
"books"=>{
"type"=>"array",
"items"=>{
"type"=>"object",
"properties"=>{
"urn" =>{ "type"=>"string" },
"title"=>{ "type"=>"string" }
}
}
}
}
}
tree = {}
def build_tree(schema, tree, level)
puts "level=#{level} schema[:type]=#{schema['type'].inspect}, schema class is #{schema.class}"
case schema['type']
when 'object'
puts "in when object for #{schema['properties'].size} properties :"
i = 0
schema['properties'].each_key{ | name | puts "#{i+=1}. #{name}" }
tree[:children] = []
schema['properties'].each do | property_name, property_schema |
puts "level=#{level} property_name=#{property_name}"
tree[:children] << { name: property_name, children: build_tree(property_schema, tree, level + 1) }
end
when 'array'
puts "in when array for #{schema['items'].size} items will process following items :"
i = 0
schema['items'].each_key{ | name | puts "#{i+=1}. #{name}" }
schema['items'].each do | property_name, property_schema |
puts "level=#{level} property_name=#{property_name}, property_schema=#{property_schema.inspect}"
tree[:children] << { name: property_name, children: build_tree(property_schema, tree, level + 1) }
end
when nil
puts "in when nil"
tree[:name] == schema
end
end
build_tree(schema, tree, 1)
puts tree
结果就是你得到的:
$ ruby -w t_a.rb
level=1 schema[:type]="object", schema class is Hash
in when object for 1 properties :
1. books
level=1 property_name=books
level=2 schema[:type]="array", schema class is Hash
in when array for 2 items will process following items :
1. type
2. properties
level=2 property_name=type, property_schema="object"
level=3 schema[:type]=nil, schema class is String
in when nil
level=2 property_name=properties, property_schema={"urn"=>{"type"=>"string"}, "title"=>{"type"=>"string"}}
level=3 schema[:type]=nil, schema class is Hash
in when nil
{:children=>[
{ :name=>"type", :children=>false},
{ :name=>"properties", :children=>false},
{ :name=>"books",
:children=>{
"type"=>"object",
"properties"=>{
"urn"=>{"type"=>"string"},
"title"=>{"type"=>"string"}
}
}
}
]
}
(注意:我已经手动漂亮地打印了结果树)。
跟踪显示发生了什么:在when 'array' 中,当您编写schema['items'].each 时,您可能想要遍历多个项目。但是没有项目,只有一个哈希。所以schema['items'].each 变成了对键的迭代。然后,您使用没有 'type' 键的模式递归,因此 case schema['type'] 属于 when nil。
请注意,如果 when 'object' 被递归调用而不是 when nil,tree[:children] = [] 将删除以前的结果,因为您始终使用相同的初始 tree。要堆叠中间结果,您需要在递归调用中提供新变量。
理解递归的最好方法不是循环到方法的开头,而是想象一连串的调用:
method_1
|
+------> method_2
|
+------> method_3
如果您将相同的初始参数作为参数传递给递归调用,它会被最后一个返回值擦除。但是如果你传递一个新变量,你可以在累加操作中使用它。
如果您检查 schema['items'] 确实是一个数组,就像我在我的解决方案中所做的那样,您会看到输入与预期不符:
$ ruby -w t.rb
level=1 schema[:type]="object", schema class is Hash
in when object for 1 properties :
1. books
level=1 property_name=books
level=2 schema[:type]="array", schema class is Hash
in when array
oops ! Array expected
{:children=>[{:name=>"books", :children=>"oops ! Array expected"}]}
现在我的解决方案。我把化妆品的细节留给你。
schema =
{ "type"=>"object",
"properties"=>{
"books"=>{
"type"=>"array",
"items"=> [ # <----- added [
{ "type"=>"object",
"properties" => {
"urn" => { "type"=>"string" },
"title" => { "type"=>"string" }
}
},
{ "type"=>"object",
"properties" => {
"urn2" => { "type"=>"string" },
"title2" => { "type"=>"string" }
}
}
] # <----- added ]
} # end books
} # end properties
} # end schema
tree = {"name"=>"200", children: []}
def build_tree(schema, tree, level)
puts
puts "level=#{level} schema[:type]=#{schema['type'].inspect}, schema class is #{schema.class}"
puts "level=#{level} tree=#{tree}"
case schema['type']
when 'object'
puts "in when object for #{schema['properties'].size} properties :"
i = 0
schema['properties'].each_key{ | name | puts "#{i+=1}. #{name}" }
schema['properties'].each do | property_name, property_schema |
puts "object level=#{level}, property_name=#{property_name}"
type, sub_tree = build_tree(property_schema, {children: []}, level + 1)
puts "object level=#{level} after recursion, type=#{type} sub_tree=#{sub_tree}"
child = { name: property_name + type }
child[:children] = sub_tree unless sub_tree.empty?
tree[:children] << child
end
puts "object level=#{level} about to return tree=#{tree}"
tree
when 'array'
puts "in when array"
case schema['items']
when Array
puts "in when Array for #{schema['items'].size} items"
i = 0
items = []
schema['items'].each do | a_hash |
puts "item #{i+=1} has #{a_hash.keys.size} keys :"
a_hash.keys.each{ | key | puts key }
# if the item has "type"=>"object" and "properties"=>{ ... }, then
# the whole item must be passed as argument to the next recursion
puts "level=#{level} about to recurs for item #{i}"
answer = build_tree(a_hash, {children: []}, level + 1)
puts "level=#{level} after recurs, answer=#{answer}"
items << { "item #{i}" => answer }
end
return ' (array)', items
else
puts "oops ! Array expected"
"oops ! Array expected"
end
when 'string'
puts "in when string, schema=#{schema}"
return ' (string)', []
else
puts "in else"
tree[:name] == schema
end
end
build_tree(schema, tree, 1)
puts 'final result :'
puts tree
执行:
$ ruby -w t.rb
level=1 schema[:type]="object", schema class is Hash
level=1 tree={"name"=>"200", :children=>[]}
in when object for 1 properties :
1. books
object level=1, property_name=books
level=2 schema[:type]="array", schema class is Hash
level=2 tree={:children=>[]}
in when array
in when Array for 2 items
item 1 has 2 keys :
type
properties
level=2 about to recurs for item 1
level=3 schema[:type]="object", schema class is Hash
level=3 tree={:children=>[]}
in when object for 2 properties :
1. urn
2. title
object level=3, property_name=urn
level=4 schema[:type]="string", schema class is Hash
level=4 tree={:children=>[]}
in when string, schema={"type"=>"string"}
object level=3 after recursion, type= (string) sub_tree=[]
object level=3, property_name=title
level=4 schema[:type]="string", schema class is Hash
level=4 tree={:children=>[]}
in when string, schema={"type"=>"string"}
object level=3 after recursion, type= (string) sub_tree=[]
object level=3 about to return tree={:children=>[{:name=>"urn (string)"}, {:name=>"title (string)"}]}
level=2 after recurs, answer={:children=>[{:name=>"urn (string)"}, {:name=>"title (string)"}]}
item 2 has 2 keys :
type
properties
level=2 about to recurs for item 2
level=3 schema[:type]="object", schema class is Hash
level=3 tree={:children=>[]}
in when object for 2 properties :
1. urn2
2. title2
object level=3, property_name=urn2
level=4 schema[:type]="string", schema class is Hash
level=4 tree={:children=>[]}
in when string, schema={"type"=>"string"}
object level=3 after recursion, type= (string) sub_tree=[]
object level=3, property_name=title2
level=4 schema[:type]="string", schema class is Hash
level=4 tree={:children=>[]}
in when string, schema={"type"=>"string"}
object level=3 after recursion, type= (string) sub_tree=[]
object level=3 about to return tree={:children=>[{:name=>"urn2 (string)"}, {:name=>"title2 (string)"}]}
level=2 after recurs, answer={:children=>[{:name=>"urn2 (string)"}, {:name=>"title2 (string)"}]}
object level=1 after recursion, type= (array) sub_tree=[{"item 1"=>{:children=>[{:name=>"urn (string)"}, {:name=>"title (string)"}]}}, {"item 2"=>{:children=>[{:name=>"urn2 (string)"}, {:name=>"title2 (string)"}]}}]
object level=1 about to return tree={"name"=>"200", :children=>[{:name=>"books (array)", :children=>[{"item 1"=>{:children=>[{:name=>"urn (string)"}, {:name=>"title (string)"}]}}, {"item 2"=>{:children=>[{:name=>"urn2 (string)"}, {:name=>"title2 (string)"}]}}]}]}
final result :
{"name"=>"200", :children=>[{:name=>"books (array)", :children=>[{"item 1"=>{:children=>[{:name=>"urn (string)"}, {:name=>"title (string)"}]}}, {"item 2"=>{:children=>[{:name=>"urn2 (string)"}, {:name=>"title2 (string)"}]}}]}]}
结果编辑:
{"name"=>"200",
:children=>[
{
:name=>"books (array)",
:children=>[
{"item 1"=>{
:children=>[
{:name=>"urn (string)"},
{:name=>"title (string)"}
]
}
},
{"item 2"=>{
:children=>[
{:name=>"urn2 (string)"},
{:name=>"title2 (string)"}
]
}
}
]
}
]
}