平面哈希到 json 具有层次结构的哈希树
Flat hash to json hash tree with a hierarchy
我有一个像 ['country', 'city', 'street', ...]
这样的层次结构的散列
谁是我树上最后一个儿子的头。
我的数据看起来像
{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "Lyon", "street": "lyon street"},
{"country": "italy", "city": "rome", "street": "rome street1"},
...
我想把它变成像
这样的树
[
{
"france" => [
{
"paris" => {"champs elysee" => [...]},
"lyon" => {"lyon street1" => [...]}
}
]
},
{
"italy" => [
{
"rome" => {"rome street 1" => [...]},
...
}
]
},
]
并且能够更改我的层次结构
我尝试了很多东西,但我没有找到任何解决方案。我认为没有正确的方法。
编辑:
这是我的代码(根本不起作用),但这是我的想法。
require 'json'
@hierarchie = ["country", "city", "street"]
f = File.read("./data.json")
@ret = JSON.parse(f)
@tree = []
def render_as_tree
@ret.each do |datum|
ptr = @tree
@hierarchie.each do |h|
if ptr.empty?
ptr << {datum[h] => []}
end
obj = ptr.detect {|d| datum[h]}
ptr = obj[ datum[h] ]
end
end
return @tree
end
puts render_as_tree()
这段代码是我的代码的一个小例子。我的代码没有country/city/street,但更好的理解和解释。
...
表示child的数量可以动态变化。对于这个例子,我可以有 country/city/district/street/house/...
或者只是 country/city
所以我最后的数据需要是平面数据。
您可以使用 inject
或 each_with_object
遍历您的数据
data = [
{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "Lyon", "street": "lyon street"},
{"country": "italy", "city": "rome", "street": "rome street1"},
]
hierarchy = data.each_with_object({}) do |row, hsh|
country = row[:country]
city = row[:city]
street = row[:street]
hsh[country] ||= {}
hsh[country][city] ||= {}
hsh[country][city][street] ||= []
hsh[country][city][street] << "?" # put here anything you need
end
p hierarchy
#=> {"france"=>{"paris"=>{"champs elysee"=>["?"]}, "Lyon"=>{"lyon street"=>["?"]}}, "italy"=>{"rome"=>{"rome street1"=>["?"]}}}
更新:
def tree(data, hierarchy)
data.each_with_object({}) do |row, hsh|
node = hsh
hierarchy.each do |level|
val = row[level]
node[val] ||= {}
node = node[val]
end
end
end
tree(data, [:country, :city])
#=> {"france"=>{"paris"=>{}, "Lyon"=>{}}, "italy"=>{"rome"=>{}}}
tree(data, [:country, :city, :street])
#=> {"france"=>{"paris"=>{"champs elysee"=>{}}, "Lyon"=>{"lyon street"=>{}}}, "italy"=>{"rome"=>{"rome street1"=>{}}}}
require "json"
data = %|[{"country": "france", "city": "paris", "street": "champs elysee"},{"country": "france", "city": "Lyon", "street": "lyon street"},{"country": "italy", "city": "rome", "street": "rome street1"}]|
parsed = JSON.parse(data)
final = {}
parsed.each {
|h|
if final.has_key? h["country"]
if final[h["country"]].key? h["city"]
final[h["country"]][h["city"]].push h["street"]
else
final[h["country"]][h["city"]] = [ h["street"]]
end
else
final[h["country"]] = {h["city"] => [ h["street"] ]}
end
}
p final
arr = [{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "lyon", "street": "rue mandelot"},
{"country": "italy", "city": "rome", "street": "via del corso"},
{"country": "france", "city": "paris", "street": "rue montorgueil"}]
arr.each_with_object({}) {|g,h| ((h[g[:country]] ||={})[g[:city]] ||={})[g[:street]]=['x']}
#=> {"france"=>{"paris"=>{"champs elysee"=>["x"], "rue montorgueil"=>["x"]},
# "lyon"=>{"rue mandelot"=>["x"]}},
# "italy"=>{"rome"=>{"via del corso"=>["x"]}}}
@fl00r 和我都返回了一个由嵌套哈希组成的对象,而不是问题中指定的交替哈希和数组。由于每个数组都包含一个散列,因此它们没有任何用处。
感谢@ProGM,我找到了解决方案!
How to deep merge two multi-root tree structure made of array and hashes
class Tree
attr_reader :data
def initialize(data)
@data = data
end
def +(other)
Tree.new(deep_merge(@data, other.is_a?(Tree) ? other.data : other))
end
delegate :empty?, to: :data
def replace(other)
@data.replace(other.data)
end
def root
level(0)
end
def levels
output = []
i = -1
while i += 1
current_level = level(i)
break if current_level.all?(&:nil?)
output << current_level
end
output
end
def level(level_number)
all_elements_at_level(@data, level_number)
end
private
def deep_merge(a, b)
case a
when Hash
return merge_hashes(a, b) if b.is_a?(Hash)
return merge_array_hash(b, a) if b.is_a?(Array)
[b, a]
when Array
return merge_arrays(a, b) if b.is_a?(Array)
return merge_array_hash(a, b) if b.is_a?(Hash)
[b] + a
else
return [a, b] if b.is_a?(Hash)
return [a] + b if b.is_a?(Array)
a == b ? a : [a, b]
end
end
def merge_array_hash(a, b)
if a.last.is_a? Hash
a[0...-1] + [merge_hashes(a.last, b)]
else
a + [b]
end
end
def merge_hashes(a, b)
a.deep_merge(b) do |_, this_val, other_val|
deep_merge(this_val, other_val)
end
end
def merge_arrays(a, b)
keys = merge_array_keys(a, b)
hashes = merge_hashes(a.last.is_a?(Hash) ? a.last : {}, b.last.is_a?(Hash) ? b.last : {})
if hashes.empty?
keys
else
(keys - hashes.keys) + [hashes]
end
end
def merge_array_keys(a, b)
(a.reject { |e| e.is_a?(Hash) } + b.reject { |e| e.is_a?(Hash) }).uniq
end
def all_elements_at_level(data, level_number)
return ground_level(data) if level_number == 0
case data
when Hash
data.map { |_, v| all_elements_at_level(v, level_number - 1) }
when Array
data.map { |e| all_elements_at_level(e, level_number) }.flatten
end
end
def ground_level(data)
case data
when Hash
data.keys
when Array
data.map { |e| all_elements_at_level(e, 0) }.flatten
else
data
end
end
end
我有一个像 ['country', 'city', 'street', ...]
这样的层次结构的散列
谁是我树上最后一个儿子的头。
我的数据看起来像
{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "Lyon", "street": "lyon street"},
{"country": "italy", "city": "rome", "street": "rome street1"},
...
我想把它变成像
这样的树[
{
"france" => [
{
"paris" => {"champs elysee" => [...]},
"lyon" => {"lyon street1" => [...]}
}
]
},
{
"italy" => [
{
"rome" => {"rome street 1" => [...]},
...
}
]
},
]
并且能够更改我的层次结构
我尝试了很多东西,但我没有找到任何解决方案。我认为没有正确的方法。
编辑:
这是我的代码(根本不起作用),但这是我的想法。
require 'json'
@hierarchie = ["country", "city", "street"]
f = File.read("./data.json")
@ret = JSON.parse(f)
@tree = []
def render_as_tree
@ret.each do |datum|
ptr = @tree
@hierarchie.each do |h|
if ptr.empty?
ptr << {datum[h] => []}
end
obj = ptr.detect {|d| datum[h]}
ptr = obj[ datum[h] ]
end
end
return @tree
end
puts render_as_tree()
这段代码是我的代码的一个小例子。我的代码没有country/city/street,但更好的理解和解释。
...
表示child的数量可以动态变化。对于这个例子,我可以有 country/city/district/street/house/...
或者只是 country/city
所以我最后的数据需要是平面数据。
您可以使用 inject
或 each_with_object
遍历您的数据
data = [
{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "Lyon", "street": "lyon street"},
{"country": "italy", "city": "rome", "street": "rome street1"},
]
hierarchy = data.each_with_object({}) do |row, hsh|
country = row[:country]
city = row[:city]
street = row[:street]
hsh[country] ||= {}
hsh[country][city] ||= {}
hsh[country][city][street] ||= []
hsh[country][city][street] << "?" # put here anything you need
end
p hierarchy
#=> {"france"=>{"paris"=>{"champs elysee"=>["?"]}, "Lyon"=>{"lyon street"=>["?"]}}, "italy"=>{"rome"=>{"rome street1"=>["?"]}}}
更新:
def tree(data, hierarchy)
data.each_with_object({}) do |row, hsh|
node = hsh
hierarchy.each do |level|
val = row[level]
node[val] ||= {}
node = node[val]
end
end
end
tree(data, [:country, :city])
#=> {"france"=>{"paris"=>{}, "Lyon"=>{}}, "italy"=>{"rome"=>{}}}
tree(data, [:country, :city, :street])
#=> {"france"=>{"paris"=>{"champs elysee"=>{}}, "Lyon"=>{"lyon street"=>{}}}, "italy"=>{"rome"=>{"rome street1"=>{}}}}
require "json"
data = %|[{"country": "france", "city": "paris", "street": "champs elysee"},{"country": "france", "city": "Lyon", "street": "lyon street"},{"country": "italy", "city": "rome", "street": "rome street1"}]|
parsed = JSON.parse(data)
final = {}
parsed.each {
|h|
if final.has_key? h["country"]
if final[h["country"]].key? h["city"]
final[h["country"]][h["city"]].push h["street"]
else
final[h["country"]][h["city"]] = [ h["street"]]
end
else
final[h["country"]] = {h["city"] => [ h["street"] ]}
end
}
p final
arr = [{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "lyon", "street": "rue mandelot"},
{"country": "italy", "city": "rome", "street": "via del corso"},
{"country": "france", "city": "paris", "street": "rue montorgueil"}]
arr.each_with_object({}) {|g,h| ((h[g[:country]] ||={})[g[:city]] ||={})[g[:street]]=['x']}
#=> {"france"=>{"paris"=>{"champs elysee"=>["x"], "rue montorgueil"=>["x"]},
# "lyon"=>{"rue mandelot"=>["x"]}},
# "italy"=>{"rome"=>{"via del corso"=>["x"]}}}
@fl00r 和我都返回了一个由嵌套哈希组成的对象,而不是问题中指定的交替哈希和数组。由于每个数组都包含一个散列,因此它们没有任何用处。
感谢@ProGM,我找到了解决方案!
How to deep merge two multi-root tree structure made of array and hashes
class Tree
attr_reader :data
def initialize(data)
@data = data
end
def +(other)
Tree.new(deep_merge(@data, other.is_a?(Tree) ? other.data : other))
end
delegate :empty?, to: :data
def replace(other)
@data.replace(other.data)
end
def root
level(0)
end
def levels
output = []
i = -1
while i += 1
current_level = level(i)
break if current_level.all?(&:nil?)
output << current_level
end
output
end
def level(level_number)
all_elements_at_level(@data, level_number)
end
private
def deep_merge(a, b)
case a
when Hash
return merge_hashes(a, b) if b.is_a?(Hash)
return merge_array_hash(b, a) if b.is_a?(Array)
[b, a]
when Array
return merge_arrays(a, b) if b.is_a?(Array)
return merge_array_hash(a, b) if b.is_a?(Hash)
[b] + a
else
return [a, b] if b.is_a?(Hash)
return [a] + b if b.is_a?(Array)
a == b ? a : [a, b]
end
end
def merge_array_hash(a, b)
if a.last.is_a? Hash
a[0...-1] + [merge_hashes(a.last, b)]
else
a + [b]
end
end
def merge_hashes(a, b)
a.deep_merge(b) do |_, this_val, other_val|
deep_merge(this_val, other_val)
end
end
def merge_arrays(a, b)
keys = merge_array_keys(a, b)
hashes = merge_hashes(a.last.is_a?(Hash) ? a.last : {}, b.last.is_a?(Hash) ? b.last : {})
if hashes.empty?
keys
else
(keys - hashes.keys) + [hashes]
end
end
def merge_array_keys(a, b)
(a.reject { |e| e.is_a?(Hash) } + b.reject { |e| e.is_a?(Hash) }).uniq
end
def all_elements_at_level(data, level_number)
return ground_level(data) if level_number == 0
case data
when Hash
data.map { |_, v| all_elements_at_level(v, level_number - 1) }
when Array
data.map { |e| all_elements_at_level(e, level_number) }.flatten
end
end
def ground_level(data)
case data
when Hash
data.keys
when Array
data.map { |e| all_elements_at_level(e, 0) }.flatten
else
data
end
end
end