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

问题描述 投票:0回答:4

我有一个代表对象的哈希数组,作为对 API 调用的响应。我需要从一些哈希中提取数据,并且一个特定的键充当哈希对象的 id。我想将数组转换为散列,其中键作为 id,值作为具有该 id 的原始散列。

这就是我要说的:

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
    来构建本地哈希的方法。

我的作图方法:

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

我忍不住觉得有一种更高效、更简洁的方法来做到这一点。当需要访问的记录数量极少时,选项 2 的性能非常好。映射在这里表现出色,但当响应中有大量记录时,它就会开始崩溃。值得庆幸的是,我预计记录不会超过 50-100 条,因此映射就足够了。

在 Ruby 中是否有更智能、更简洁或更高效的方法来执行此操作?

ruby arrays performance hash
4个回答
24
投票

红宝石<= 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 中,唉,不幸的是

请注意,您的用例由 Active Support 涵盖:Enumerable#index_by

红宝石>= 2.1

[更新] 仍然不喜欢

Enumerable#mash
,但现在我们有了Array#to_h。它创建了一个中间数组,但总比没有好:

> object = api_response.map { |r| [r[:id], r] }.to_h

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"

您在问题末尾展示的方式是另一种有效的方式。两者都相当快速且优雅。


0
投票

为此我可能会去:

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

块中的多个括号并不是非常漂亮,但它只需要 api_response 的一次迭代就可以解决问题。


0
投票
ideal_response = {}
api_response.each do |response|
  ideal_response[response[:id]] = response
end

O(n),其中 n 是 api_response 的长度

© www.soinside.com 2019 - 2024. All rights reserved.