数组散列 Ruby

Posted

技术标签:

【中文标题】数组散列 Ruby【英文标题】:Array to Hash Ruby 【发布时间】:2011-05-01 00:14:46 【问题描述】:

转换这个数组:

a = ["item 1", "item 2", "item 3", "item 4"] 

...到一个哈希:

 "item 1" => "item 2", "item 3" => "item 4" 

even 索引处的元素是 keysodd 元素是 values

【问题讨论】:

【参考方案1】:
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # =>  "item 1" => "item 2", "item 3" => "item 4" 

就是这样。 * 称为 splat 运算符。

@Mike Lewis 的一个警告(在 cmets 中):“对此要非常小心。Ruby 会扩展堆栈上的 splats。如果您使用大型数据集执行此操作,预计会炸毁您的堆栈。”

因此,对于大多数一般用例来说,这种方法很好,但如果您想对大量数据进行转换,请使用不同的方法。例如,@Łukasz Niemier(也在 cmets 中)为大型数据集提供了这种方法:

h = Hash[a.each_slice(2).to_a]

【讨论】:

@tester,* 被称为 splat 运算符。它接受一个数组并将其转换为项目的文字列表。所以*[1,2,3,4] => 1, 2, 3, 4。在这个例子中,上面相当于做Hash["item 1", "item 2", "item 3", "item 4"]。而Hash 有一个[] 方法,它接受参数列表(使偶数索引键和奇数索引值),但Hash[] 不接受数组,所以我们使用* 分解数组。 对此要非常小心。 Ruby 扩展堆栈上的 splats。如果您使用大型数据集执行此操作,预计会耗尽您的堆栈。 在大数据表上可以使用Hash[a.each_slice(2).to_a] “爆仓”是什么意思? @Kevin,stack 使用程序分配和保留用于某些特定操作的一小块内存。最常见的是,它用于保存迄今为止已调用的方法的堆栈。这就是术语堆栈跟踪的由来,这也是为什么无限递归方法会导致堆栈溢出。这个答案中的方法也使用了栈,但是由于栈只是内存的一小块区域,如果你用一个大数组尝试这个方法,它会填满栈并导致错误(与堆栈溢出)。【参考方案2】:

Ruby 2.1.0 在 Array 上引入了 to_h 方法,如果您的原始数组由键值对数组组成,则该方法可以满足您的要求:http://www.ruby-doc.org/core-2.1.0/Array.html#method-i-to_h。

[[:foo, :bar], [1, 2]].to_h
# => :foo => :bar, 1 => 2

【讨论】:

漂亮!比这里的其他一些解决方案要好得多。 对于 2.1.0 之前的 ruby​​ 版本,您可以使用 Hash::[] 方法获得类似的结果,只要您有一对嵌套数组即可。所以 a =[[:foo, :1], [bar, 2]] --- Hash[a] => :foo=>1, :bar=>2 @AfDev,确实,谢谢。你是对的(忽略小错别字:bar 需要是一个符号,而符号:2 应该是一个整数。所以,你的表达式更正为a = [[:foo, 1], [:bar, 2]])。【参考方案3】:

只需将Hash.[] 与数组中的值一起使用。例如:

arr = [1,2,3,4]
Hash[*arr] #=> gives 1 => 2, 3 => 4

【讨论】:

[*arr] 是什么意思? @Marius: *arrarr 转换成一个参数列表,所以这是以arr 的内容作为参数调用Hash 的[] 方法。【参考方案4】:

或者如果你有一个[key, value]数组,你可以这样做:

[[1, 2], [3, 4]].inject() do |r, s|
  r.merge!(s[0] => s[1])
end # =>  1 => 2, 3 => 4 

【讨论】:

您的回答与问题无关,在您的情况下,使用相同的Hash[*arr] 仍然容易得多 不。它将返回 [1, 2] => [3, 4] 。而且由于问题的标题是“Array to Hash”,而内置的“Hash to Array”方法确实如此: 1 => 2, 3 => 4.to_a # => [[1, 2], [3, 4]],我想不止一个人可以在这里尝试得到内置的“Hash to Array”的倒数“ 方法。其实,反正我就是这样结束的。 抱歉,我添加了一个备用星号。 Hash[arr] 将为您完成这项工作。 恕我直言更好的解决方案:Hash[*array.flatten(1)] Yossi:很抱歉让死人复活,但他的回答还有一个更大的问题,那就是使用#inject 方法。对于#merge!,应该使用#each_with_object。如果坚持使用#inject,则应该使用#merge 而不是#merge!【参考方案5】:

这是我在谷歌上搜索时所寻找的:

[a: 1, b: 2].reduce() |h, v| h.merge v => :a=>1, :b=>2

【讨论】:

您不想使用merge,它会在每次循环迭代时构造并丢弃一个新的哈希值,而且速度非常慢。如果您有一个哈希数组,请尝试[a:1,b:2].reduce(, :merge!) - 它会将所有内容合并到相同的(新)哈希中。 谢谢,这也是我想要的! :) 你也可以.reduce(&:merge!) [a: 1, b: 2].reduce(&:merge!) 计算结果为 :a=>1, :b=>2 之所以有效,是因为注入/减少具有可以省略参数的功能,在这种情况下,它使用数组的第一个参数来操作 as 输入参数,并将数组的其余部分作为数组。将它与 symbol-to-proc 结合起来,你就得到了这个简洁的结构。换句话说,[a: 1, b: 2].reduce(&:merge!)[a: 1, b: 2].reduce |m, x| m.merge(x) 相同,[b: 2].reduce(a: 1) |m, x| m.merge(x) [b: 2].reduce(a: 1) |m, x| m.merge(x) 相同。【参考方案6】:

Enumerator 包括Enumerable。由于2.1Enumerable也有一个方法#to_h。这就是为什么,我们可以这样写:-

a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => "item 1"=>"item 2", "item 3"=>"item 4"

因为#each_slice没有block给了我们Enumerator,按照上面的解释,我们可以在Enumerator对象上调用#to_h方法。

【讨论】:

【参考方案7】:

你可以这样尝试,对于单个数组

irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
  => ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
  => "item 1"=>"item 2", "item 3"=>"item 4"

对于数组的数组

irb(main):022:0> a = [[1, 2], [3, 4]]
  => [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
  => 1=>2, 3=>4

【讨论】:

【参考方案8】:
a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map  |e| e  ]

或者,如果你讨厌Hash[ ... ]

a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end

或者,如果你是一个破函数式编程的懒惰粉丝:

h = a.lazy.each_slice( 2 ).tap  |a|
  break Hash.new  |h, k| h[k] = a.find  |e, _| e == k [1] 

#=> 
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"

【讨论】:

如果您不完全讨厌Hash[ ... ],但想将其用作链式方法(就像您可以使用to_h 一样),您可以结合Boris 的建议并写:arr.each_slice( 2 ).map |e| e .tap |a| break Hash[a] 跨度> 为了使上面代码的语义更清晰:这将创建一个“惰性哈希”h,它最初是,并且会拉取需要时从原始数组 a 中取出元素。只有这样它们才会真正存储在 h!【参考方案9】:

所有答案都假定起始数组是唯一的。 OP 没有指定如何处理具有重复条目的数组,这会导致重复键。

我们来看看:

a = ["item 1", "item 2", "item 3", "item 4", "item 1", "item 5"]

您将丢失 item 1 => item 2 对,因为它被覆盖 bij item 1 => item 5

Hash[*a]
=> "item 1"=>"item 5", "item 3"=>"item 4"

包括reduce(&:merge!) 在内的所有方法都会导致相同的删除。

不过,这可能正是您所期望的。但在其他情况下,您可能希望得到一个带有Array 值的结果:

"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]

天真的方法是创建一个辅助变量,一个具有默认值的哈希,然后将其填充到循环中:

result = Hash.new |hash, k| hash[k] = []  # Hash.new with block defines unique defaults.
a.each_slice(2) |k,v| result[k] << v 
a
=> "item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]

也许可以在一行中使用assocreduce 完成上述操作,但这变得更加难以推理和阅读。

【讨论】:

以上是关于数组散列 Ruby的主要内容,如果未能解决你的问题,请参考以下文章

散列在散列上

ruby 将嵌套数组转换为哈希

使用键数组遍历嵌套的 Ruby 哈希

Ruby中数组哈希的所有可能组合

Ruby 按布尔值和数字排序

在 Ruby 中解析来自 Eventbrite API 的 JSON 响应