Array to Hash Ruby
好吧,这是交易,我在谷歌上搜索了很久,想找到解决办法,虽然有很多人,但他们似乎没有做我要找的工作。
基本上我有一个这样的数组
1
| ["item 1","item 2","item 3","item 4"] |
我想把这个转换成散列,所以看起来像这样
1
| {"item 1" =>"item 2","item 3" =>"item 4" } |
即"偶数"索引上的项是键,"奇数"索引上的项是值。
有什么办法可以干净利落地做到这一点吗?我认为一个蛮力的方法是将所有偶数索引提取到一个单独的数组中,然后循环它们以添加值。
1 2
| a = ["item 1","item 2","item 3","item 4"]
h = Hash[*a] # => {"item 1" =>"item 2","item 3" =>"item 4" } |
就是这样。*被称为splat运算符。
每个@mike lewis都要注意一点(在评论中):"要非常小心。Ruby在堆栈上展开展开。如果您对一个大数据集执行此操作,那么应该将您的堆栈吹出。"
所以,对于大多数一般的用例来说,这个方法是很好的,但是如果您想对大量数据进行转换,可以使用不同的方法。例如,@?Ukasz Niemier(也在注释中)为大型数据集提供了这种方法:
1
| h = Hash[a.each_slice(2).to_a] |
- 在这个例子中,*做了什么?
- @测试人员,*被称为splat操作符。它接受一个数组并将其转换为项目的文本列表。所以*[1,2,3,4]=>1, 2, 3, 4。在这个例子中,上面的操作相当于执行Hash["item 1","item 2","item 3","item 4"]。并且Hash有一个[]方法,它接受一个参数列表(生成偶数个索引键和奇数个索引值),但是Hash[]不接受一个数组,所以我们使用*来显示数组。
- @测试人员对splat的另一个常见用例是逆向使用,以定义参数数量未知的方法。如def whatever *args。你可以输入一个列表,比如whatever(7, 1, 3, 4)。如上所述,*args表示一个列表,args表示一个数组。因为你在传递一个列表,你实际上是在传递*args。因此,args是一个数组,最终得到的是args == [7, 1, 3, 4]。因此,您可以将您想要的任何参数传入方法,然后使用args访问它们。
- 小心点。Ruby在堆栈上展开展开。如果你用一个大的数据集来完成这项工作,那么你的堆栈就会被吹走。
- @米凯莱维斯,谢谢你的警告。我在我的答案中加上了这一点(加上归因于你)。
- 另一个警告是,如果传入元素数为奇数的数组,则此方法会引发异常。ArgumentError: odd number of arguments for Hash
- 在大数据表上,您可以使用Hash[a.each_slice(2).to_a]。
- @?Ukaszniemier,谢谢。我把它编辑成了我的答案(加上归因于你)
- "炸掉你的烟囱"是什么意思?
- @Kevin,堆栈使用一小块内存,程序为某些特定操作分配和保留这些内存。最常见的是,它用于保存迄今为止被调用的方法的堆栈。这是术语堆栈跟踪的起源,这也是无限递归方法可能导致堆栈溢出的原因。这个答案中的方法也使用堆栈,但是由于堆栈只是一个很小的内存区域,如果用一个大数组来尝试这个方法,它将填满堆栈并导致一个错误(沿着堆栈溢出的同一行的错误)。
- 现在可以进行a.each_slice(2).to_h(至少在yarv中)。
- 简单的代码示例,在尝试使用spat发送大量参数时查看错误:ruby-forum.com/topic/1210854
- 我觉得这个答案再也不正确了。答案开头的链接文档显示,to_h在一组数组上运行。运行["item 1","item 2","item 3","item 4"].to_h导致TypeError Exception: wrong element type String at 0 (expected array)。不过,[["item 1","item 2"], ["item 3","item 4"]].to_h起作用了。我认为@hauleth是对的,我用Ruby2.5.3测试的正确答案是a.each_slice(2).to_h。
- 是的,似乎不再工作了。删除了那个答案。离开了我原来的哈希splat答案,因为这从来没有停止工作,而且是我原来的答案无论如何。
- 另外,我的答案中已经列出了each_slice(2).to_h方法。
Ruby2.1.0在数组上引入了一个to_h方法,如果原始数组由键值对数组组成,那么它可以满足您的需要:http://www.ruby-doc.org/core-2.1.0/array.html method-i-to-u h。
1 2
| [[: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]]。
只需对数组中的值使用Hash.[]。例如:
1 2
| arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4} |
- [*arr]是什么意思?
- @马吕斯:*arr把arr转换成一个参数列表,所以这就是调用[]散列方法,其中arr的内容作为参数。
或者,如果您有一个[key, value]数组数组,则可以执行以下操作:
1 2 3
| [[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] }。既然问题的标题是"数组到散列",而内置的"散列到数组"方法是:{ 1 => 2, 3 => 4}.to_a # => [[1, 2], [3, 4]],我认为不止一个方法可以在这里结束,试图得到内置的"散列到数组"方法的倒数。实际上,我就是这样结束的。
- 对不起,我加了一个星号。埃多克斯1〔32〕将为你做这项工作。
- 哦。。。我懂了。。。对不起/谢谢。
- imho更好的解决方案:hash[*array.flatten(1)]
- 尤西:很抱歉把死者抬起来,但他的回答有一个更大的问题,那就是使用#inject方法。对于#merge!,应该使用#each_with_object。如果坚持使用#inject,应该使用#merge,而不是#merge!。
这是我在谷歌搜索时要找的:
[{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}的评价
- 这是因为inject/reduce有一个特性,您可以省略参数,在这种情况下,它使用数组的第一个参数作为输入参数进行操作,而数组的其余部分作为数组。把它和符号结合起来进行处理,就得到了这个简洁的结构。换言之,[{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) }相同。
- 两个注意事项:(1)这会使数组的原始第一个元素发生变化。如果不想这样做,您要么显式地传入{},要么使用与上述性能问题相同的非变异merge;(2)输入数组需要至少有一个元素,否则将返回nil,而不是空哈希
Enumerator包括Enumerable。由于2.1,Enumerable也有一个方法#to_h。所以,我们可以写:
1 2 3
| a = ["item 1","item 2","item 3","item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2","item 3"=>"item 4"} |
因为没有block的#each_slice给了我们Enumerator,根据上面的解释,我们可以在Enumerator对象上调用#to_h方法。
您可以这样尝试,对于单个数组
1 2 3 4
| 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"} |
用于数组
1 2 3 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} |
1 2
| a = ["item 1","item 2","item 3","item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ] |
或者,如果你讨厌Hash[ ... ]:
1
| a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end |
或者,如果你是一个懒散的功能编程爱好者:
1 2 3 4 5 6
| 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] }。
- 为了使上面代码的语义更清晰:这将创建一个"lazy hash"h,它最初是空的,并在需要时从原始数组a中拉出元素。只有这样,它们才会真正存储在H中!