在Ruby中将数组转换为哈希的最佳方法是什么

在Ruby中将数组转换为哈希的最佳方法是什么

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}

推荐阅读