What is the best way to convert an array to a hash in Ruby
在Ruby中,给定以下形式之一的数组...
1 2
| [apple, 1, banana, 2]
[[apple, 1], [banana, 2]] |
...什么是将其转换为哈希形式的最佳方法...
1
| {apple => 1, banana => 2} |
只需使用Hash[*array_variable.flatten]
例如:
1 2 3 4 5 6 7
| a1 = ['apple', 1, 'banana', 2]
h1 = Hash[*a1.flatten(1)]
puts"h1: #{h1.inspect}"
a2 = [['apple', 1], ['banana', 2]]
h2 = Hash[*a2.flatten(1)]
puts"h2: #{h2.inspect}" |
使用Array#flatten(1)限制了递归,因此Array键和值可以按预期工作。
注意:有关简洁高效的解决方案,请参阅Marc-Andr? Lafortune的答案如下。
该答案最初是作为使用flatten方法的替代方法提供的,在撰写本文时,flatten是获得最高评价的方法。我应该澄清的是,我不打算将此示例作为最佳实践或有效方法。原始答案如下。
警告!使用flatten的解决方案将不会保留Array键或值!
以@John Topley的流行答案为基础,让我们尝试:
1 2
| a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten] |
这将引发错误:
1 2 3
| ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10 |
构造函数期望一个长度均匀的数组(例如['k1','v1,'k2','v2'])。更糟糕的是,变平为均匀长度的另一个Array只会默默地为我们提供具有错误值的Hash。
如果要使用数组键或值,可以使用map:
1 2
| h3 = Hash[a3.map {|key, value| [key, value]}]
puts"h3: #{h3.inspect}" |
这将保留Array键:
1
| h3: {["orange","seedless"]=>3,"apple"=>1,"banana"=>2} |
最好的方法是使用Array#to_h:
1
| [ [:apple,1],[:banana,2] ].to_h #=> {apple: 1, banana: 2} |
请注意,to_h也接受一个块:
1 2
| [:apple, :banana].to_h { |fruit| [fruit,"I like #{fruit}s"] }
# => {apple:"I like apples", banana:"I like bananas"} |
注意:to_h在Ruby 2.6.0中接受一个块;对于早期的ruby,您可以使用我的backportsruby和require 'backports/2.6.0/enumerable/to_h'
Ruby 2.1.0中引入了不带块的
to_h。
在Ruby 2.1之前,可以使用不太清晰的Hash[]:
1 2
| array = [ [:apple,1],[:banana,2] ]
Hash[ array ] #= > {:apple => 1, :banana => 2} |
最后,要警惕使用flatten的任何解决方案,这可能会产生数组本身值的问题。
更新
Ruby 2.1.0今天发布。我附带了Array#to_h(发行说明和ruby-doc),它解决了将Array转换为Hash的问题。
Ruby文档示例:
1
| [[:foo, :bar], [1, 2]].to_h # => {:foo => :bar, 1 => 2} |
Edit: Saw the responses posted while I was writing, Hash[a.flatten] seems the way to go.
Must have missed that bit in the documentation when I was thinking through the response. Thought the solutions that I've written can be used as alternatives if required.
第二种形式更简单:
1 2
| a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r } |
a =数组,h =哈希,r =返回值哈希(我们在其中累加的哈希),i =数组中的项
我想到的第一种形式的最简洁的方式是这样的:
1 2 3
| a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last } |
您还可以使用以下方法将2D数组简单地转换为哈希值:
1 2 3 4 5 6 7
| 1.9.3p362 :005 > a= [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
1.9.3p362 :006 > h = Hash[a]
=> {1=>2, 3=>4} |
概要
追加答案,但使用匿名数组并进行注释:
1
| Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)] |
从内部开始,分开回答:
-
"a,b,c,d"实际上是一个字符串。
-
split用逗号分隔成一个数组。
-
zip以及以下数组。
-
[1,2,3,4]是实际的数组。
中间结果是:
1
| [[a,1],[b,2],[c,3],[d,4]] |
flatten然后将其转换为:
1
| ["a",1,"b",2,"c",3,"d",4] |
然后:
*["a",1,"b",2,"c",3,"d",4]将其展开为
"a",1,"b",2,"c",3,"d",4
我们可以将其用作Hash[]方法的参数:
1
| Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)] |
其结果是:
1
| {"a"=>1,"b"=>2,"c"=>3,"d"=>4} |
如果您的数组看起来像这样-
1
| data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]] |
,并且您希望每个数组的第一个元素成为哈希键,其余元素成为值数组,那么您可以执行以下操作-
1 2 3
| data_hash = Hash[data.map { |key| [key.shift, key] }]
#=>{"foo"=>[1, 2, 3, 4],"bar"=>[1, 2],"foobar"=>[1,"*", 3, 5, :foo]} |
不确定这是否是最好的方法,但这可行:
1 2 3 4 5 6 7 8 9 10 11
| a = ["apple", 1,"banana", 2]
m1 = {}
for x in (a.length / 2).times
m1[a[x*2]] = a[x*2 + 1]
end
b = [["apple", 1], ["banana", 2]]
m2 = {}
for x,y in b
m2[x] = y
end |
如果数值是seq索引,那么我们可以有更简单的方法...
这是我的代码提交,我的Ruby有点生锈
1 2 3 4 5 6
| input = ["cat", 1,"dog", 2,"wombat", 3]
hash = Hash.new
input.each_with_index {|item, index|
if (index%2 == 0) hash[item] = input[index+1]
}
hash #=> {"cat"=>1,"wombat"=>3,"dog"=>2} |
|