我有一系列哈希:
connections = [
{:name=>"John Doe", :number=>"5551234567", :count=>8},
{:name=>"Jane Doe", :number=>"5557654321", :count=>6},
{:name=>"John Doe", :number=>"5559876543", :count=>3}
]
如果:name值是重复的,就像John Doe一样,它应该将:number值组合成一个数组。计数不再重要,因此输出应采用以下格式:
{"John Doe"=>["5551234567","5559876543"],
"Jane Doe"=>["5557654321"]}
到目前为止我所拥有的是:
k = connections.inject(Hash.new{ |h,k| h[k[:name]] = [k[:number]] }) { |h,(k,v)| h[k] << v ; h }
但这只是输出
{"John Doe"=>["5559876543", nil], "Jane Doe"=>["5557654321", nil]}
答案 0 :(得分:2)
在一行中:
k = connections.each.with_object({}) {|conn,result| (result[conn[:name]] ||= []) << conn[:number] }
更具可读性:
result = Hash.new {|h,k| h[k] = [] }
connections.each {|conn| result[conn[:name]] << conn[:number] }
result #=> {"John Doe"=>["5551234567", "5559876543"], "Jane Doe"=>["5557654321"]}
答案 1 :(得分:1)
names = {}
connections.each{ |c| names[c[:name]] ||= []; names[c[:name]].push(c[:number]) }
puts names
答案 2 :(得分:1)
这有效:
connections.group_by do |h|
h[:name]
end.inject({}) do |h,(k,v)|
h.merge( { k => (v.map do |i| i[:number] end) } )
end
# => {"John Doe"=>["5551234567", "5559876543"], "Jane Doe"=>["5557654321"]}
一步一步......
connections
与您的帖子相同:
connections
# => [{:name=>"John Doe", :number=>"5551234567", :count=>8},
# {:name=>"Jane Doe", :number=>"5557654321", :count=>6}, {:name=>"John Doe",
# :number=>"5559876543", :count=>3}]
首先,我们使用group_by
将哈希条目与相同的:name
:
connections.group_by do |h| h[:name] end
# => {"John Doe"=>[{:name=>"John Doe", :number=>"5551234567", :count=>8},
# {:name=>"John Doe", :number=>"5559876543", :count=>3}],
# "Jane Doe"=>[{:name=>"Jane Doe", :number=>"5557654321", :count=>6}]}
这很好,但我们希望结果哈希的值只是显示为:number
键值的数字,而不是完整的原始条目哈希值。
只给出一个列表值,我们就可以通过这种方式获得所需的结果:
[{:name=>"John Doe", :number=>"5551234567", :count=>8},
{:name=>"John Doe", :number=>"5559876543", :count=>3}].map do |i|
i[:number]
end
# => ["5551234567", "5559876543"]
但我们希望一次对所有列表值执行此操作,同时保持与其键的关联。它基本上是一个嵌套的map
操作,但外部映射跨越Hash而不是Array。
事实上你可以用map
来做。唯一棘手的部分是Hash上的map
不返回哈希,而是一组嵌套的[
键,
值]
数组。通过将调用包装在Hash[
... ]
构造函数中,您可以将结果转回哈希:
Hash[
connections.group_by do |h|
h[:name]
end.map do |k,v|
[ k, (v.map do |i| i[:number] end) ]
end
]
返回与上面原始完整答案相同的结果,并且可以说更清晰,因此您可能只想使用该版本。
但我使用的机制是inject
。它类似于map
,但它不是只返回块中返回值的数组,而是让您完全控制如何从单个块调用中构造返回值:
connections.group_by do |h|
h[:name]
end.inject({}) do |h,(k,v)|
h.merge( { k => (v.map do |i| i[:number] end) } )
end
这会创建一个新的Hash,它开始为空({}
传递给inject
),并将其传递给do
块(显示为h
})以及group_by
返回的哈希中的第一个键/值对。该块创建另一个新的Hash,其中传入单个键,并且如上所述转换值的结果,并将其合并到传入的值中,返回新值 - 基本上,它添加一个新的键/值对哈希,内部map
将值转换为所需的形式。新的Hash从块中返回,因此它将在下一次通过块时成为h
的新值。
(我们也可以直接用h
将条目分配到h[k] = v.map ...
,但随后该块需要return h
作为单独的语句,因为它是返回值块,而不是块执行结束时h
的值,传递给下一次迭代。)
顺便说一句:我在我的街区周围使用了do
... end
代替{
... }
,以避免与{
混淆... }
用于哈希文字。没有语义差异;这完全是一种风格问题。在标准的Ruby样式中,您可以将{
... }
用于单行块,并将do
... end
限制为跨越多行的块