【发布时间】:2011-05-01 00:14:46
【问题描述】:
转换这个数组:
a = ["item 1", "item 2", "item 3", "item 4"]
...到一个哈希:
{ "item 1" => "item 2", "item 3" => "item 4" }
即even 索引处的元素是 keys,odd 元素是 values。
【问题讨论】:
转换这个数组:
a = ["item 1", "item 2", "item 3", "item 4"]
...到一个哈希:
{ "item 1" => "item 2", "item 3" => "item 4" }
即even 索引处的元素是 keys,odd 元素是 values。
【问题讨论】:
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }
就是这样。 * 称为 splat 运算符。
@Mike Lewis 的一个警告(在 cmets 中):“对此要非常小心。Ruby 会扩展堆栈上的 splats。如果您使用大型数据集执行此操作,预计会炸毁您的堆栈。”
因此,对于大多数一般用例来说,这种方法很好,但如果您想对大量数据进行转换,请使用不同的方法。例如,@Łukasz Niemier(也在 cmets 中)为大型数据集提供了这种方法:
h = Hash[a.each_slice(2).to_a]
【讨论】:
* 被称为 splat 运算符。它接受一个数组并将其转换为项目的文字列表。所以*[1,2,3,4] => 1, 2, 3, 4。在这个例子中,上面相当于做Hash["item 1", "item 2", "item 3", "item 4"]。而Hash 有一个[] 方法,它接受参数列表(使偶数索引键和奇数索引值),但Hash[] 不接受数组,所以我们使用* 分解数组。
Hash[a.each_slice(2).to_a]。
Ruby 2.1.0 在 Array 上引入了 to_h 方法,如果您的原始数组由键值对数组组成,则该方法可以满足您的要求: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]])。
只需将Hash.[] 与数组中的值一起使用。例如:
arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4}
【讨论】:
*arr 将arr 转换成一个参数列表,所以这是以arr 的内容作为参数调用Hash 的[] 方法。
或者如果你有一个[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) } 与[{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没有block给了我们Enumerator,按照上面的解释,我们可以在Enumerator对象上调用#to_h方法。
【讨论】:
你可以这样尝试,对于单个数组
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 一样),您可以结合Boris 的建议并写: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 值的结果:
{"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 完成上述操作,但这变得更加难以推理和阅读。
【讨论】: