简体   繁体   English

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

[英]Convert array-of-hashes to a hash-of-hashes, indexed by an attribute of the hashes

I've got an array of hashes representing objects as a response to an API call. 我有一个表示对象的哈希数组作为对API调用的响应。 I need to pull data from some of the hashes, and one particular key serves as an id for the hash object. 我需要从一些哈希中提取数据,并且一个特定的键用作哈希对象的id。 I would like to convert the array into a hash with the keys as the ids, and the values as the original hash with that id. 我想将数组转换为哈希,键为ids,值为原始哈希值。

Here's what I'm talking about: 这就是我所说的:

api_response = [
  { :id => 1, :foo => 'bar' },
  { :id => 2, :foo => 'another bar' },
  # ..
]

ideal_response = {
  1 => { :id => 1, :foo => 'bar' },
  2 => { :id => 2, :foo => 'another bar' },
  # ..
}

There are two ways I could think of doing this. 我有两种方法可以想到这样做。

  1. Map the data to the ideal_response (below) 将数据映射到ideal_response (下面)
  2. Use api_response.find { |x| x[:id] == i } 使用api_response.find { |x| x[:id] == i } api_response.find { |x| x[:id] == i } for each record I need to access. api_response.find { |x| x[:id] == i }我需要访问的每条记录。
  3. A method I'm unaware of, possibly involving a way of using map to build a hash, natively. 我不知道的一种方法,可能涉及一种使用map来构建哈希的方法。

My method of mapping: 我的映射方法:

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

I can't help but feel like there is a more performant, tidier way of doing this. 我不禁感到有一种更高效,更整洁的方式。 Option 2 is very performant when there are a very minimal number of records that need to be accessed. 当需要访问的记录数量非常少时,选项2非常高效。 Mapping excels here, but it starts to break down when there are a lot of records in the response. 映射优于此处,但当响应中有大量记录时,它会开始崩溃。 Thankfully, I don't expect there to be more than 50-100 records, so mapping is sufficient. 值得庆幸的是,我不希望有超过50-100条记录,因此映射就足够了。

Is there a smarter, tidier, or more performant way of doing this in Ruby? 在Ruby中有更聪明,更整洁或更高效的方法吗?

Ruby <= 2.0 Ruby <= 2.0

> Hash[api_response.map { |r| [r[:id], r] }]
#=> {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}} 

However, Hash::[] is pretty ugly and breaks the usual left-to-right OOP flow. 但是, Hash :: []非常丑陋,打破了通常从左到右的OOP流程。 That's why Facets proposed Enumerable#mash : 这就是为什么Facets提出了Enumerable #mash

> require 'facets'
> api_response.mash { |r| [r[:id], r] }
#=> {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}} 

This basic abstraction (convert enumerables to hashes) was asked to be included in Ruby long ago, alas, without luck . 这个基本的抽象(将枚举变为哈希)被要求在很久以前被包含在Ruby中,唉, 没有运气

Ruby >= 2.1 Ruby> = 2.1

[UPDATE] Still no love for Enumerable#mash , but now we have Array#to_h . [更新]仍然不喜欢Enumerable#mash #mash ,但现在我们有阵列#to_h Not ideal -because we need an intermediate array- but better than nothing: 不理想 - 因为我们需要一个中间阵列 - 但总比没有好:

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

For this I'd probably just go: 为此,我可能会去:

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

Not super pretty with the multiple brackets in the block but it does the trick with just a single iteration of the api_response. 块中的多个括号不是很漂亮,但只需一次api_response迭代即可完成。

Something like: 就像是:

ideal_response = api_response.group_by{|i| i[:id]} 
#=> {1=>[{:id=>1, :foo=>"bar"}], 2=>[{:id=>2, :foo=>"another bar"}]}

It uses Enumerable's group_by , which works on collections, returning matches for whatever key value you want. 它使用Enumerable的group_by ,它适用于集合,返回所需键值的匹配。 Because it expects to find multiple occurrences of matching key-value hits it appends them to arrays, so you end up with a hash of arrays of hashes. 因为它期望找到多次匹配的键值命中,所以它将它们附加到数组,因此最终得到哈希数组的哈希值。 You could peel back the internal arrays if you wanted but could run a risk of overwriting content if two of your hash IDs collided. 如果需要,可以剥离内部数组,但如果两个哈希ID发生冲突,可能会有覆盖内容的风险。 group_by avoids that with the inner array. group_by避免使用内部数组。

Accessing a particular element is easy: 访问特定元素很简单:

ideal_response[1][0]       #=> {:id=>1, :foo=>"bar"}
ideal_response[1][0][:foo] #=> "bar"

The way you show at the end of the question is another valid way of doing it. 您在问题结尾处显示的方式是另一种有效的方法。 Both are reasonably fast and elegant. 两者都相当快速和优雅。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM