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

我有一个表示对象的哈希数组作为对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 } api_response.find { |x| x[:id] == i }我需要访问的每条记录。
  3. 我不知道的一种方法,可能涉及一种使用map来构建哈希的方法。

我的映射方法:

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

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

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

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中,唉, 没有运气 。

Ruby> = 2.1

[更新]仍然不喜欢Enumerable#mash #mash ,但现在我们有arrays#to_h 。 不理想 – 因为我们需要一个中间arrays – 但总比没有好:

 # ruby 2.1 api_response.map { |r| [r[:id], r] }.to_h 

就像是:

 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" 

你在问题的最后展示的方式是另一种有效的方法。 两者都相当快速和优雅。

为此,我可能会去:

 ideal_response = api_response.each_with_object(Hash.new) { |o, h| h[o[:id]] = o } 

块中的多个括号不是很漂亮,但它只需要api_response的一次迭代即可完成。