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

Nat*_*itz 120 ruby arrays hashmap

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

[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
Run Code Online (Sandbox Code Playgroud)

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

{apple => 1, banana => 2}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ley 144

简单地使用 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}"
Run Code Online (Sandbox Code Playgroud)

使用Array#flatten(1)限制递归,因此Array键和值按预期工作.

  • 警告:如果需要数组键或值,使用展平的答案会导致问题. (11认同)
  • FWIW,如果你真的想要(更多)一个尺寸适合所有版本,你也可以使用`Hash [*ary.flatten(1)]`,这将保留数组键和值.它是递归的"flatten",正在摧毁那些,这很容易避免. (8认同)
  • 最好不要尝试为此做一个全面的解决方案.如果你的键和值在[[key1,value1],[key2,value2]]中配对,那么只需将它传递给Hash []而不加肥.哈希[a2] ==哈希[*a2.flatten].如果数组已经被展平,如[key1,value1,key2,value2]那么只需在var前加上*,Hash [*a1] (5认同)
  • 哦,口才!这就是我喜欢Ruby的原因 (4认同)

Ste*_*tew 88

注意:有关简洁有效的解决方案,请参阅下面的Marc-AndréLafortune的答案.

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


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

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

a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Run Code Online (Sandbox Code Playgroud)

这会引发错误:

ArgumentError: odd number of arguments for Hash
        from (irb):10:in `[]'
        from (irb):10
Run Code Online (Sandbox Code Playgroud)

构造函数期望一个偶数长度的数组(例如['k1','v1,'k2','v2']).更糟糕的是,一个扁平到均匀长度的不同数组会默默地给我们一个不正确值的哈希值.

如果要使用数组键或值,可以使用map:

h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
Run Code Online (Sandbox Code Playgroud)

这会保留Array键:

h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
Run Code Online (Sandbox Code Playgroud)

  • 这与Hash [a3]相同,因为a3 == a3.map {| k,v | [k,v]}是真的,它实际上相当于a3.dup. (15认同)
  • 这个答案效率不高.它也已经过时了.看我的回答. (3认同)
  • 而不是使用地图,为什么不指定展平的深度?例如:`h3 = Hash [*a3.flatten(1)]`而不是`h3 = Hash [*a3.flatten]`会抛出错误. (2认同)
  • 是的,我认为 Marc-André 的 `to_h` 更好。 (2认同)

Mar*_*une 76

最好的方法是使用Array#to_h:

[ [:apple,1],[:banana,2] ].to_h  #=> {apple: 1, banana: 2}
Run Code Online (Sandbox Code Playgroud)

注意:这是在Ruby 2.1.0中引入的.对于较旧的Ruby,您可以使用我的to_hgem to_h,或者使用backports:

[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] } 
  # => {apple: "I like apples", banana: "I like bananas"}
Run Code Online (Sandbox Code Playgroud)

这在Ruby 1.8.7及更高版本中可用.如果您仍在使用Ruby 1.8.6 require 'backports/2.6.0/enumerable/to_h',那么您也可以使用to_hbackport.

最后,虽然许多解决方案都使用Hash[],但这可能会产生数组本身的问题.

  • 感谢新.to_h方法的简单性! (4认同)
  • 我认为这应该是接受的答案 (4认同)
  • 我比上面的答案更喜欢`to_h`方法,因为它表达了在***操作数组后转换***的意图. (3认同)

小智 19

更新

Ruby 2.1.0今天发布.我附带Array#to_h(发行说明ruby-doc),它解决了转换Array为a 的问题Hash.

Ruby docs示例:

[[:foo, :bar], [1, 2]].to_h    # => {:foo => :bar, 1 => 2}
Run Code Online (Sandbox Code Playgroud)


Dae*_*min 9

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

第二种形式更简单:

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

a =数组,h =哈希,r =返回值哈希(我们累积的哈希),i =数组中的项目

我能想到做第一个表单的最好方法是这样的:

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

  • 'a.inject({})`one-liner的+1,允许更灵活的值赋值. (2认同)

小智 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} 
Run Code Online (Sandbox Code Playgroud)


lin*_*des 5

总结 & TL; DR:

这个答案希望是对其他答案信息的全面总结。

考虑到问题中的数据以及一些额外内容,非常简短的版本:

flat_array   = [  apple, 1,   banana, 2  ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [  apple, 1,   banana     ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana   ] ] # count=2 of either k or k,v arrays


# there's one option for flat_array:
h1  = Hash[*flat_array]                     # => {apple=>1, banana=>2}

# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0    => {apple=>1, banana=>2}
h2b = Hash[nested_array]                    # => {apple=>1, banana=>2}

# ok if *only* the last value is missing:
h3  = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4  = Hash[incomplete_n] # or .to_h           => {apple=>1, banana=>nil}

# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3  # => false
h3 == h4  # => true
Run Code Online (Sandbox Code Playgroud)

讨论和细节如下。


设置:变量

为了显示我们将预先使用的数据,我将创建一些变量来表示数据的各种可能性。它们分为以下几类:

基于直接在问题中的内容,如a1a2

(注意:我认为appleandbanana是为了表示变量。正如其他人所做的那样,我将从这里开始使用字符串,以便输入和结果可以匹配。)

flat_array   = [  apple, 1,   banana, 2  ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [  apple, 1,   banana     ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana   ] ] # count=2 of either k or k,v arrays


# there's one option for flat_array:
h1  = Hash[*flat_array]                     # => {apple=>1, banana=>2}

# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0    => {apple=>1, banana=>2}
h2b = Hash[nested_array]                    # => {apple=>1, banana=>2}

# ok if *only* the last value is missing:
h3  = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4  = Hash[incomplete_n] # or .to_h           => {apple=>1, banana=>nil}

# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3  # => false
h3 == h4  # => true
Run Code Online (Sandbox Code Playgroud)

多值键和/或值,如a3

在其他一些答案中,提出了另一种可能性(我在这里进行了扩展)——键和/或值本身可能是数组:

a1 = [  'apple', 1 ,  'banana', 2  ] # flat input
a2 = [ ['apple', 1], ['banana', 2] ] # key/value paired input
Run Code Online (Sandbox Code Playgroud)

不平衡数组,如a4

为了更好的衡量,我想我会为我们可能有不完整输入的情况添加一个:

a3 = [ [ 'apple',                   1   ],
       [ 'banana',                  2   ],
       [ ['orange','seedless'],     3   ],
       [ 'pear',                 [4, 5] ],
     ]
Run Code Online (Sandbox Code Playgroud)

现在,工作:

从初始平面数组开始,a1

有些人建议使用#to_h(它出现在 Ruby 2.1.0 中,并且可以向后移植到早期版本)。对于初始平面阵列,这不起作用:

a4 = [ [ 'apple',                   1],
       [ 'banana',                  2],
       [ ['orange','seedless'],     3],
       [ 'durian'                    ], # a spiky fruit pricks us: no value!
     ]
Run Code Online (Sandbox Code Playgroud)

通过Hash::[]结合图示运营商的作用:

a1.to_h   # => TypeError: wrong element type String at 0 (expected array)
Run Code Online (Sandbox Code Playgroud)

这就是由 表示的简单情况的解决方案a1

使用键/值对数组,a2

对于[key,value]类型数组的数组,有两种方法。

首先,Hash::[]仍然有效(就像它一样*a1):

Hash[*a1] # => {"apple"=>1, "banana"=>2}
Run Code Online (Sandbox Code Playgroud)

然后#to_h现在也有效:

Hash[a2] # => {"apple"=>1, "banana"=>2}
Run Code Online (Sandbox Code Playgroud)

因此,对于简单的嵌套数组情况,有两个简单的答案。

即使将子数组作为键或值,这仍然是正确的,例如a3

a2.to_h  # => {"apple"=>1, "banana"=>2}
Run Code Online (Sandbox Code Playgroud)

但是榴莲有尖刺(异常结构会产生问题):

如果我们得到的输入数据不平衡,我们将遇到以下问题#to_h

Hash[a3] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]} 
a3.to_h  # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
Run Code Online (Sandbox Code Playgroud)

Hash::[]仍然有效,只需设置nildurian(以及 a4 中的任何其他数组元素,它只是一个 1 值数组)的值:

a4.to_h  # => ArgumentError: wrong array length at 3 (expected 2, was 1)
Run Code Online (Sandbox Code Playgroud)

展平 - 使用新变量a5a6

提到的其他一些答案flatten,带或不带1参数,让我们创建一些新变量:

Hash[a4] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
Run Code Online (Sandbox Code Playgroud)

我选择a4用作基础数据是因为我们遇到了平衡问题,它以a4.to_h. 我认为调用flatten可能是某人用来尝试解决该问题的一种方法,可能如下所示。

flatten不带参数 ( a5):

a5 = a4.flatten
# => ["apple", 1, "banana", 2,  "orange", "seedless" , 3, "durian"] 
a6 = a4.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian"] 
Run Code Online (Sandbox Code Playgroud)

在一个天真的看,这似乎工作-但它让我们过上出师不利与无核桔子,从而也使得3一个durian一个

而这,与a1,只是行不通:

Hash[*a5]       # => {"apple"=>1, "banana"=>2, "orange"=>"seedless", 3=>"durian"}
# (This is the same as calling `Hash[*a4.flatten]`.)
Run Code Online (Sandbox Code Playgroud)

所以a4.flatten对我们没有用,我们只是想使用Hash[a4]

flatten(1)情况下(a6):

但是只有部分展平怎么办?值得注意的,在调用Hash::[]使用splat的局部扁平阵列(上a6)是一样的调用Hash[a4]

a5.to_h # => TypeError: wrong element type String at 0 (expected array)
Run Code Online (Sandbox Code Playgroud)

预展平的数组,仍然嵌套(另一种获取方式a6):

但是,如果这就是我们首先获得数组的方式呢?(也就是说,与 相比a1,它是我们的输入数据——只是这次一些数据可以是数组或其他对象。)我们已经看到这Hash[*a6]是行不通的,但是如果我们仍然想要获得最后一个元素(重要!见下文)作为nil值的键?

在这种情况下,仍然有一种方法可以做到这一点,使用Enumerable#each_slice让我们回到键/值作为外部数组中的元素:

Hash[*a6] # => ArgumentError: odd number of arguments for Hash
Run Code Online (Sandbox Code Playgroud)

请注意,这最终会为我们提供一个不“相同a4但具有相同值的新数组:

a7 = a6.each_slice(2).to_a
# => [["apple", 1], ["banana", 2], [["orange", "seedless"], 3], ["durian"]] 
Run Code Online (Sandbox Code Playgroud)

因此我们可以再次使用Hash::[]

a4.equal?(a7) # => false
a4 == a7      # => true
Run Code Online (Sandbox Code Playgroud)

但是有个问题!

重要的是要注意,each_slice(2)如果最后一个键是缺少值的键,则该解决方案只会使事情恢复正常。如果我们后来添加了一个额外的键/值对:

Hash[a7] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
# or Hash[a6.each_slice(2).to_a]
Run Code Online (Sandbox Code Playgroud)

我们从中得到的两个散列在重要方面是不同的:

a4_plus = a4.dup # just to have a new-but-related variable name
a4_plus.push(['lychee', 4])
# => [["apple",                1],
#     ["banana",               2],
#     [["orange", "seedless"], 3], # multi-value key
#     ["durian"],              # missing value
#     ["lychee", 4]]           # new well-formed item

a6_plus = a4_plus.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian", "lychee", 4]

a7_plus = a6_plus.each_slice(2).to_a
# => [["apple",                1],
#     ["banana",               2],
#     [["orange", "seedless"], 3], # so far so good
#     ["durian",               "lychee"], # oops! key became value!
#     [4]]                     # and we still have a key without a value

a4_plus == a7_plus # => false, unlike a4 == a7
Run Code Online (Sandbox Code Playgroud)

(注意:我使用awesome_print'sap只是为了更容易在此处显示结构;对此没有概念要求。)

因此,each_slice不平衡平坦输入的解决方案仅在不平衡位位于最后时才有效。


外卖:

  1. 只要有可能,将这些东西的输入设置为[key, value]成对(外部数组中每个项目的子数组)。
  2. 当您确实可以做到这一点时,其中一个#to_hHash::[]两个都可以。
  3. 如果你不能,Hash::[]结合 splat ( *) 就可以了,只要输入是平衡的
  4. 使用不平衡平面数组作为输入,唯一合理的方法是最后 value一项是唯一缺少的项。

旁注:我发布这个答案是因为我觉得有一些价值需要补充——一些现有的答案有不正确的信息,而且(我读过的)没有一个答案像我在这里努力做的那样完整。我希望它有帮助。尽管如此,我还是要感谢那些在我之前的人,其中一些人为本答案的部分内容提供了灵感。