如何转换看起来像这样的Ruby Hash:
{
:axis => [1,2],
:"coord.x" => [12,13],
:"coord.y" => [14,15],
}
进入这个:
{
:axis => [1,2], #unchaged from input (ok)
:coord => #this has become a hash from coord.x and coord.y keys above
{
:x => [12,13]
:y => [14,15]
}
}
我不知道从哪里开始!
答案 0 :(得分:5)
# {"a.b.c"=>"v", "b.c.d"=>"c"} ---> {:a=>{:b=>{:c=>"v"}}, :b=>{:c=>{:d=>"c"}}}
def flat_keys_to_nested(hash)
hash.each_with_object({}) do |(key,value), all|
key_parts = key.split('.').map!(&:to_sym)
leaf = key_parts[0...-1].inject(all) { |h, k| h[k] ||= {} }
leaf[key_parts.last] = value
end
end
答案 1 :(得分:3)
此代码可能需要重构,但它适用于您提供的输入。
hash = {
:axis => [1,2],
"coord.x" => [12,13],
"coord.y" => [14,15],
}
new_hash = {}
hash.each do |key, val|
new_key, new_sub_key = key.to_s.split('.')
new_key = new_key.to_sym
unless new_sub_key.nil?
new_sub_key = new_sub_key.to_sym
new_hash[new_key] = {} if new_hash[new_key].nil?
new_hash[new_key].merge!({new_sub_key => val})
else
new_hash.store(key, val)
end
end
new_hash # => {:axis=>[1, 2], :coord=>{:x=>[12, 13], :y=>[14, 15]}}
答案 2 :(得分:1)
Ruby的优点在于你可以用不同的方式做事。这是另一个(但我测量 - 稍慢,虽然这取决于散列大小)方法:
hash = {
:axis => [1,2],
"coord.x" => [12,13],
"coord.y" => [14,15],
}
new_hash = Hash.new { |hash, key| hash[key] = {} }
hash.each do |key, value|
if key.respond_to? :split
key.split('.').each_slice(2) do |new_key, sub_key|
new_hash[new_key.to_sym].store(sub_key.to_sym, value)
end
next
end
new_hash[key] = value
end
puts new_hash # => {:axis=>[1, 2], :coord=>{:x=>[12, 13], :y=>[14, 15]}}
但是,至少对我来说,理解正在发生的事情更容易,更快捷。所以这是个人的事情。
答案 3 :(得分:1)
经过一些测试后,我发现如果你有更深层次的结构,你最终会遇到这些算法的问题,因为拆分钥匙只能占一个点('。&#39 ;)在钥匙。如果您有更多a
,则算法会失败。
例如,给定:
a.b.c
你会期待:
{
'a' => 'a',
'b.a' => 'b.a',
'b.b' => 'b.b',
'c.a.b.c.d' => 'c.a.b.c.d',
'c.a.b.c.e' => 'c.a.b.c.e'
}
如果数据试图用标量覆盖哈希值,反之亦然,也会出现问题:
{
'a' => 'a',
'b' => {'a' =>'b.a', 'b' => 'b.b'},
'c' => {
'a' => {
'b' => {
'c' => {
'd' => 'c.a.b.c.d',
'e' => 'c.a.b.c.e'
}
}
}
}
}
或
{
'a3.b.c.d' => 'a3.b.c.d',
'a3.b' => 'a3.b'
}
这是最终版本。如果发生其中一个不良情况,这个会引发参数错误。显然,如果有意义的话,你可以捕获坏数据版本,只回显原始哈希值。
{
'a4.b' => 'a4.b',
'a4.b.c.d' => 'a4.b.c.d'
}
答案 4 :(得分:0)
本着模块化和可重用性的精神,我提出了另一种解决方案。在第一种方法中,我们可以编写一个反向散列构造函数:
input_hash.map do |main_key, main_value|
main_key.to_s.split(".").reverse.inject(main_value) do |value, key|
{key.to_sym => value}
end
end
# [{:coord=>{:x=>[12, 13]}}, {:coord=>{:y=>[14, 15]}}, {:axis=>[1, 2]}]
不是你想要的,但非常接近。只有当Ruby有哈希的递归合并时,我们才能完成。 Ruby没有这样的方法,但毫无疑问,其他人需要它并编写some solutions。选择你最喜欢的实现,现在只需写下:
input_hash.map do |main_key, main_value|
main_key.to_s.split(".").reverse.inject(main_value) do |value, key|
{key.to_sym => value}
end
end.inject(&:deep_merge)
# {:coord=>{:y=>[14, 15], :x=>[12, 13]}, :axis=>[1, 2]}
答案 5 :(得分:0)
这是@grosser答案的稍微重构版本:
def flatten(hash)
hash.each_with_object({}) do |(path,value), all|
*path, key = key.split('.').map!(&:to_sym)
leaf = path.inject(all) { |h, k| h[k] ||= {} }
leaf[key] = value
end
end