在Ruby中将数组转换为哈希的最佳方法是什么?
在Ruby中,给定一个以下forms之一的数组…
[apple, 1, banana, 2] [[apple, 1], [banana, 2]]
什么是最好的方式将其转换为哈希forms…
{apple => 1, banana => 2}
注意 :为了简洁和高效的解决scheme,请参阅下面的Marc-AndréLafortune的答案 。
这个答案最初是作为使用扁平化的方法的一种替代方法提出的,在写作时这个方法是最高的。 我应该澄清,我不打算把这个例子作为最佳实践或有效的方法。 原来的答案如下。
警告! 使用拼合的解决scheme不会保留数组键或值!
基于@John Topley的stream行答案,让我们试试:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ] h3 = Hash[*a3.flatten]
这会引发一个错误:
ArgumentError: odd number of arguments for Hash from (irb):10:in `[]' from (irb):10
构造函数期待一个偶数长度的数组(例如['k1','v1,'k2','v2'])。 更糟糕的是,一个不同的数组被放平到一个长度,只会默默地给我们一个带有不正确值的哈希。
如果你想使用数组键或值,你可以使用map :
h3 = Hash[a3.map {|key, value| [key, value]}] puts "h3: #{h3.inspect}"
这保留了数组键:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
只需使用Hash[*array_variable.flatten]
例如:
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)
限制recursion,所以Array
键和值按预期方式工作。
最好的方法是使用Array#to_h
:
[ [:apple,1],[:banana,2] ].to_h #=> {:apple => 1, :banana => 2}
注意 :这是在Ruby 2.1.0中引入的。 对于较老的Ruby,你可以使用我的backports
gem并且require 'backports/2.1.0/array/to_h'
,否则使用Hash[]
:
array = [ [:apple,1],[:banana,2] ] Hash[ array ] #= > {:apple => 1, :banana => 2}
这在Ruby 1.8.7及更高版本中可用。 如果你仍然在使用Ruby 1.8.6,你可能require "backports/1.8.7/hash/constructor"
,但是你也可以使用to_h
backport。
最后,虽然许多解决scheme使用flatten
,但这可能会导致数组本身的值出现问题。
更新
Ruby 2.1.0今天发布 。 我用Array#to_h
( 发行说明和ruby-doc )来解决将Array
转换为Hash
。
Ruby文档示例:
[[:foo, :bar], [1, 2]].to_h # => {:foo => :bar, 1 => 2}
编辑:看到我写作时发布的回复,哈希[a.flatten]似乎是要走的路。 当我正在思考答案时,一定是错过了文档中的那一点。 认为我写的解决scheme可以作为替代品,如果需要的话。
第二种forms更简单:
a = [[:apple, 1], [:banana, 2]] h = a.inject({}) { |r, i| r[i.first] = i.last; r }
a =数组,h =散列,r =返回值散列(我们积累的散列),i =数组中的项
我能想到做第一种forms的最好办法就是这样的:
a = [:apple, 1, :banana, 2] h = {} a.each_slice(2) { |i| h[i.first] = i.last }
你也可以简单地把二维数组转换成哈希使用:
1.9.3p362 :005 > a= [[1,2],[3,4]] => [[1, 2], [3, 4]] 1.9.3p362 :006 > h = Hash[a] => {1=>2, 3=>4}
附加到答案,但使用匿名数组和注释:
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]
除了这个答案,从内部开始:
-
"a,b,c,d"
实际上是一个string。 -
split
逗号分隔成一个数组。 -
zip
与下面的数组一起。 -
[1,2,3,4]
是一个实际的数组。
中间结果是:
[[a,1],[b,2],[c,3],[d,4]]
然后将其变换为:
["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[]
方法的参数:
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]
这产生:
{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
不知道这是否是最好的方法,但是这是有效的:
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
如果你有像这样的数组 –
data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]]
你想要每个数组的第一个元素成为哈希键和其余的元素成为值数组,然后你可以做这样的事情 –
data_hash = Hash[data.map { |key| [key.shift, key] }] #=>{"foo"=>[1, 2, 3, 4], "bar"=>[1, 2], "foobar"=>[1, "*", 3, 5, :foo]}
如果数值是seq索引,那么我们可以有更简单的方法…这是我的代码提交,我的ruby有点生疏
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}