在Ruby中将数组转换为散列的最佳方法是什么?

时间:2008-09-02 13:53:55

标签: ruby arrays hashmap

在Ruby中,给定一个以下形式之一的数组......

[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]

...将此转换为以...形式的哈希的最佳方式是什么?

{apple => 1, banana => 2}

12 个答案:

答案 0 :(得分:143)

只需使用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)限制递归,以便Array键和值按预期工作。

答案 1 :(得分:86)

注意:要获得简明有效的解决方案,请参阅下面的Marc-André Lafortune's answer

这个答案最初是作为使用flatten的方法的替代方案而提供的,这些方法在撰写本文时是最受欢迎的。我应该澄清一点,我不打算将这个例子作为最佳实践或有效方法。原始答案如下。


警告!使用展平的解决方案不会保留数组键或值!

在@John Topley的热门回答的基础上,让我们试试:

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'])。更糟糕的是,一个扁平到均匀长度的不同数组会默默地给我们一个不正确值的哈希值。

如果您想使用数组键或值,可以使用地图

h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"

这会保留数组键:

h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}

答案 2 :(得分:70)

最好的方法是使用Array#to_h

[ [:apple,1],[:banana,2] ].to_h  #=> {apple: 1, banana: 2}

请注意to_h也接受一个块:

[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] } 
  # => {apple: "I like apples", banana: "I like bananas"}

注意to_h接受Ruby 2.6.0+中的块;对于早期红宝石,您可以使用我的backports宝石和require 'backports/2.6.0/enumerable/to_h'

Ruby 2.1.0中引入了没有块的

to_h

在Ruby 2.1之前,可以使用不太清晰的Hash[]

array = [ [:apple,1],[:banana,2] ]
Hash[ array ]  #= > {:apple => 1, :banana => 2}

最后,要小心使用flatten的任何解决方案,这可能会产生数组本身值的问题。

答案 3 :(得分:19)

<强>更新

Ruby 2.1.0 is released today。我附带Array#to_hrelease notesruby-doc),它解决了将Array转换为Hash的问题。

Ruby docs示例:

[[:foo, :bar], [1, 2]].to_h    # => {:foo => :bar, 1 => 2}

答案 4 :(得分:9)

  

编辑:看到我写作时发布的回复,哈希[a.flatten]似乎要走了。   当我在思考响应时,一定是错过了文档中的那一点。如果需要,我认为我编写的解决方案可以作为替代方案。

第二种形式更简单:

a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r }

a = array,h = hash,r = return-value hash(我们累积的那个),i =数组中的item

我能想到做第一种形式的最好方法是这样的:

a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last }

答案 5 :(得分:5)

您还可以使用以下方法将2D数组转换为哈希:

1.9.3p362 :005 > a= [[1,2],[3,4]]

 => [[1, 2], [3, 4]]

1.9.3p362 :006 > h = Hash[a]

 => {1=>2, 3=>4} 

答案 6 :(得分:3)

附加答案,但使用匿名数组并注释:

Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]

将答案分开,从内部开始:

  • "a,b,c,d"实际上是一个字符串。
  • 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}

答案 7 :(得分:0)

不确定这是否是最好的方式,但这有效:

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

答案 8 :(得分:0)

如果你有这样的数组 -

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

答案 9 :(得分:0)

arr.each_with_object({}) { |value, hash| hash[key] = value }

这比他们使用基准测试中的 .map 和 hash[] 的最佳答案快约 4 - 5 倍

答案 10 :(得分:0)

对于性能和内存分配问题,请检查 my answerRails mapping array of hashes onto single hash,我对几个解决方案进行了基准测试。

reduce / inject 可以是最快或最慢的解决方案,具体取决于您使用哪种方法。

答案 11 :(得分:-1)

如果数值是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}