Answers:
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }
而已。在*
被称为图示操作。
每个@Mike Lewis一个警告(在评论中):“请务必小心。Ruby会在堆栈上扩展splats。如果使用大型数据集进行扩展,则可能会炸毁堆栈。”
因此,对于大多数一般使用情况,此方法很好,但是如果要对大量数据进行转换,请使用其他方法。例如,@ŁukaszNiemier(也在注释中)为大型数据集提供此方法:
h = Hash[a.each_slice(2).to_a]
Hash[a.each_slice(2).to_a]
。
Ruby 2.1.0 to_h
在Array上引入了一种方法,该方法可以在原始数组由键值对数组组成的情况下满足您的要求:http : //www.ruby-doc.org/core-2.1.0/Array.html#method -i-to_h。
[[:foo, :bar], [1, 2]].to_h
# => {:foo => :bar, 1 => 2}
bar
需要是一个符号,并且该符号:2
应该是整数。因此,更正后的表达式是a = [[:foo, 1], [:bar, 2]]
)。
或者,如果您有一个数组[key, value]
数组,则可以执行以下操作:
[[1, 2], [3, 4]].inject({}) do |r, s|
r.merge!({s[0] => s[1]})
end # => { 1 => 2, 3 => 4 }
Hash[*arr]
{ [1, 2] => [3, 4] }
。而且,由于问题的标题为“ Array to Hash”,而内置的“ Hash to Array”方法具有:{ 1 => 2, 3 => 4}.to_a # => [[1, 2], [3, 4]]
,所以我想尝试获得内置的“ Hash to Array”方法的逆向可能不止于此。实际上,这就是我到这里结束的方式。
Hash[arr]
将为您完成工作。
#inject
方法的使用。使用#merge!
,#each_with_object
应该已经使用过。如果#inject
坚持,#merge
而不是#merge!
应该使用。
这是我在搜索时寻找的东西:
[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v }
=> {:a=>1, :b=>2}
merge
,它会在每次循环迭代时构造并丢弃新的哈希,并且非常慢。如果您有一系列哈希,请尝试[{a:1},{b:2}].reduce({}, :merge!)
-将所有内容合并到相同(新)的哈希中。
.reduce(&:merge!)
[{a: 1}, {b: 2}].reduce(&:merge!)
评估为{:a=>1, :b=>2}
[{a: 1}, {b: 2}].reduce(&:merge!)
是一样的[{a: 1}, {b: 2}].reduce { |m, x| m.merge(x) }
,其是相同的[{b: 2}].reduce({a: 1}) { |m, x| m.merge(x) }
。
Enumerator
包括Enumerable
。既然2.1
,Enumerable
也有办法#to_h
。这就是为什么,我们可以写:-
a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2", "item 3"=>"item 4"}
因为#each_slice
没有块给了我们Enumerator
,并且根据上面的解释,我们可以#to_h
在Enumerator
对象上调用方法。
您可以尝试这样,对于单个数组
irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
=> ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
=> {"item 1"=>"item 2", "item 3"=>"item 4"}
用于数组的数组
irb(main):022:0> a = [[1, 2], [3, 4]]
=> [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
=> {1=>2, 3=>4}
a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ]
或者,如果您讨厌Hash[ ... ]
:
a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end
或者,如果您懒于对功能编程的破坏,请执行以下操作:
h = a.lazy.each_slice( 2 ).tap { |a|
break Hash.new { |h, k| h[k] = a.find { |e, _| e == k }[1] }
}
#=> {}
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"
Hash[ ... ]
但想将其用作链接方法(例如您可以使用to_h
),则可以结合鲍里斯的建议并写出:arr.each_slice( 2 ).map { |e| e }.tap { |a| break Hash[a] }
所有答案均假设起始数组是唯一的。OP没有指定如何处理具有重复条目的数组,这会导致重复键。
让我们看一下:
a = ["item 1", "item 2", "item 3", "item 4", "item 1", "item 5"]
您将失去item 1 => item 2
对,因为它被覆盖bij item 1 => item 5
:
Hash[*a]
=> {"item 1"=>"item 5", "item 3"=>"item 4"}
所有方法,包括reduce(&:merge!)
相同删除的结果。
不过,这可能正是您所期望的。但是在其他情况下,您可能希望获取带有Array
for值的结果:
{"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}
天真的方法是创建一个帮助程序变量,即具有默认值的哈希,然后将其填充到循环中:
result = Hash.new {|hash, k| hash[k] = [] } # Hash.new with block defines unique defaults.
a.each_slice(2) {|k,v| result[k] << v }
a
=> {"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}
可以单行使用assoc
和reduce
执行上面的操作,但这使推理和阅读变得更加困难。
*
称为splat运算符。它接受一个数组并将其转换为项目的文字列表。所以*[1,2,3,4]
=>1, 2, 3, 4
。在此示例中,以上等效于doHash["item 1", "item 2", "item 3", "item 4"]
。并且Hash
有一个[]
方法可以接受参数列表(产生偶数索引键和奇数索引值),但是Hash[]
不接受数组,因此我们使用splat数组*
。