将哈希数组转换为散列哈希值,由哈希值的属性索引

时间:2011-01-20 23:38:42

标签: ruby arrays performance hash

我有一个表示对象的哈希数组作为对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' },
  # ..
}

我有两种方法可以想到这样做。

  1. 将数据映射到ideal_response(下方)
  2. 对我需要访问的每条记录使用api_response.find { |x| x[:id] == i }
  3. 我不知道的一种方法,可能涉及一种使用map本地构建哈希的方法。
  4. 我的映射方法:

    keys = data.map { |x| x[:id] }
    mapped = Hash[*keys.zip(data).flatten]
    

    我不禁觉得有更高效,更整洁的方式。当需要访问的记录数量非常少时,选项2非常高效。映射优于此处,但当响应中有大量记录时,它会开始崩溃。值得庆幸的是,我预计不会有超过50-100条记录,因此映射就足够了。

    在Ruby中有更智能,更整洁或更高效的方法吗?

3 个答案:

答案 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迭代即可完成。