在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}