我有一个哈希,比方说,
account = {
name: "XXX",
email: "xxx@yyy.com",
details: {
phone: "9999999999",
dob: "00-00-00",
address: "zzz"
}
}
现在我想将account
转换为这样的哈希:
account = {
name: "XXX",
email: "xxx@yyy.com",
phone: "9999999999",
dob: "00-00-00",
address: "zzz"
}
我是初学者,想知道是否有任何功能可以做到这一点? (除了合并嵌套哈希然后删除它)
答案 0 :(得分:3)
您可以实现一种通用flatten_hash
方法,该方法与Array#flatten
的工作方式大致类似,因为它可以展平任意深度的哈希值。
def flatten_hash(hash, &block)
hash.dup.tap do |result|
hash.each_pair do |key, value|
next unless value.is_a?(Hash)
flattened = flatten_hash(result.delete(key), &block)
result.merge!(flattened, &block)
end
end
end
在这里,我们仍在执行删除/合并序列,但无论如何它都需要在任何此类实现中,即使隐藏在进一步的抽象之下也是如此。
您可以按如下方式使用此方法:
account = {
name: "XXX",
email: "xxx@yyy.com",
details: {
phone: "9999999999",
dob: "00-00-00",
address: "zzz"
}
}
flatten(account)
# => {:name=>"XXX", :email=>"xxx@yyy.com", :phone=>"9999999999", :dob=>"00-00-00", :address=>"zzz"}
请注意,使用此方法时,默认情况下,较低级别哈希中的任何键都会覆盖上级哈希中的现有键。但是,您可以提供一个块来解决任何合并冲突。请参阅documentation of Hash#merge!
了解如何使用此功能。
答案 1 :(得分:1)
这样可以解决问题:
account.map{|k,v| k==:details ? v : {k => v}}.reduce({}, :merge)
答案 2 :(得分:0)
案例1:account
的每个值都可能是值不是哈希值的哈希值
account.flat_map { |k,v| v.is_a?(Hash) ? v.to_a : [[k,v]] }.to_h
#=> {:name=>"XXX", :email=>"xxx@yyy.com", :phone=>"9999999999",
# :dob=>"00-00-00", :address=>"zzz"}
案例2:account
可能有嵌套哈希
def doit(account)
recurse(account.to_a).to_h
end
def recurse(arr)
arr.each_with_object([]) { |(k,v),a|
a.concat(v.is_a?(Hash) ? recurse(v.to_a) : [[k,v]]) }
end
account = {
name: "XXX",
email: "xxx@yyy.com",
details: {
phone: "9999999999",
dob: { a: 1, b: { c: 2, e: { f: 3 } } },
address: "zzz"
}
}
doit account
#=> {:name=>"XXX", :email=>"xxx@yyy.com", :phone=>"9999999999", :a=>1,
# :c=>2, :f=>3, :address=>"zzz"}
案例1的解释
计算进展如下。
在这里使用Enumerable#flat_map的一种方法是,对于某些方法g
,
[a, b, c].map { |e| g(e) } #=> [f, g, h]
其中a
,b
,c
,f
,g
和h
都是数组,那么
[a, b, c].flat_map { |e| g(e) } #=> [*f, *g, *h]
让我们首先创建一个枚举器,将元素传递给块。
enum = account.to_enum
#=> #<Enumerator: {:name=>"XXX", :email=>"xxx@yyy.com",
# :details=>{:phone=>"9999999999", :dob=>"00-00-00",
# :address=>"zzz"}}:each>
enum
生成一个传递给块的元素,并将块变量设置为等于这些值。
k, v = enum.next
#=> [:name, "XXX"]
k #=> :name
v #=> "XXX"
v.is_a?(Hash)
#=> false
a = [[k,v]]
#=> [[:name, "XXX"]]
k, v = enum.next
#=> [:email, "xxx@yyy.com"]
v.is_a?(Hash)
#=> false
b = [[k,v]]
#=> [[:email, "xxx@yyy.com"]]
k,v = enum.next
#=> [:details, {:phone=>"9999999999", :dob=>"00-00-00", :address=>"zzz"}]
v.is_a?(Hash)
#=> true
c = v.to_a
#=> [[:phone, "9999999999"], [:dob, "00-00-00"], [:address, "zzz"]]
d = account.flat_map { |k,v| v.is_a?(Hash) ? v.to_a : [[k,v]] }
#=> [*a, *b, *c]
#=> [[:name, "XXX"], [:email, "xxx@yyy.com"], [:phone, "9999999999"],
# [:dob, "00-00-00"], [:address, "zzz"]]
d.to_h
#=> <the return value shown above>