数组散列 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 索引处的元素是 keys,odd 元素是 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:*arr
将arr
转换成一个参数列表,所以这是以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.1
,Enumerable
也有一个方法#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"]
也许可以在一行中使用assoc
和reduce
完成上述操作,但这变得更加难以推理和阅读。
【讨论】:
以上是关于数组散列 Ruby的主要内容,如果未能解决你的问题,请参考以下文章