我有一个表示对象的哈希数组作为对API调用的响应。我需要从一些哈希中提取数据,并且一个特定的键用作哈希对象的id。我想将数组转换为哈希,键为ids,值为原始哈希值。
这就是我所说的:
api_response = [
{ :id => 1, :foo => 'bar' },
{ :id => 2, :foo => 'another bar' },
# ..
]
ideal_response = {
1 => { :id => 1, :foo => 'bar' },
2 => { :id => 2, :foo => 'another bar' },
# ..
}
我有两种方法可以想到这样做。
ideal_response
(下方)api_response.find { |x| x[:id] == i }
。map
本地构建哈希的方法。我的映射方法:
keys = data.map { |x| x[:id] }
mapped = Hash[*keys.zip(data).flatten]
我不禁觉得有更高效,更整洁的方式。当需要访问的记录数量非常少时,选项2非常高效。映射优于此处,但当响应中有大量记录时,它会开始崩溃。值得庆幸的是,我预计不会有超过50-100条记录,因此映射就足够了。
在Ruby中有更智能,更整洁或更高效的方法吗?
答案 0 :(得分:20)
Ruby< = 2.0
Hash[api_response.map { |r| [r[:id], r] }]
# {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}}
然而,Hash::[]非常丑陋并打破了通常从左到右的OOP流程。这就是Facets提出Enumerable#mash:
的原因require 'facets'
api_response.mash { |r| [r[:id], r] }
# {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}}
这个基本的抽象(将枚举转换成哈希)被要求在很久以前被包含在Ruby中,唉,without luck。
Ruby> = 2.1
[更新] 仍然不爱Enumerable#mash
,但现在我们有了Array#to_h。不理想 - 因为我们需要一个中间阵列 - 但总比没有好:
# ruby 2.1
api_response.map { |r| [r[:id], r] }.to_h
答案 1 :(得分:0)
类似的东西:
ideal_response = api_response.group_by{|i| i[:id]}
#=> {1=>[{:id=>1, :foo=>"bar"}], 2=>[{:id=>2, :foo=>"another bar"}]}
它使用Enumerable的group_by
,它适用于集合,返回所需键值的匹配。因为它期望找到多次匹配的键值命中,所以它将它们附加到数组,因此最终得到哈希数组的哈希值。如果需要,可以剥离内部数组,但如果两个哈希ID发生冲突,可能会有覆盖内容的风险。 group_by
避免使用内部数组。
访问特定元素很简单:
ideal_response[1][0] #=> {:id=>1, :foo=>"bar"}
ideal_response[1][0][:foo] #=> "bar"
您在问题结尾处展示的方式是另一种有效的方式。两者都相当快速和优雅。
答案 2 :(得分:0)
为此,我可能会去:
ideal_response = api_response.each_with_object(Hash.new) { |o, h| h[o[:id]] = o }
块中的多个括号不是很漂亮,但只需一次api_response迭代即可完成。