将多维数组转换为哈希值而不覆盖值

时间:2015-06-29 01:20:34

标签: arrays ruby multidimensional-array hash

我有一个多维数组,例如:

array = [["stop", "halt"],["stop", "red"],["go", "green"],["go","fast"],["caution","yellow"]]

我想把它变成这样的哈希:

hash = {"stop" => ["halt","red"], "go" => ["green","fast"], "caution" => "yellow"}

但是,当我在array.to_h时,这些值会相互覆盖并得到:

hash = {"stop" => "red", "go" => "fast", "caution" => "yellow"}

如何获得所需的数组?

2 个答案:

答案 0 :(得分:2)

这是一种方式。它使用Enumerable#each_with_objectHash#update(又名merge!)的形式,它使用一个块来确定合并的两个哈希中存在的键的值。

array << ["stop", "or I'll fire!"]

array.each_with_object({}) { |(f,l),h|
  h.update(f=>l) { |_,ov,nv| ov.is_a?(Array) ? ov << nv : [ov, nv] } }
  #=> {"stop"=>["halt", "red", "or I'll fire!"],
  #    "go"=>["green", "fast"],
  #    "caution"=>"yellow"}

如果您希望返回的哈希值中的所有值都是数组(即"caution"=>["yellow"]),则代码会被简化,这通常对后续计算更方便:

array.each_with_object({}) { |(f,l),h|  h.update(f=>[l]) {|_,ov,nv| ov+nv }}
  #=> {"stop"=>["halt", "red", "or I'll fire!"],
  #    "go"=>["green", "fast"],
  #    "caution"=>["yellow"]}

答案 1 :(得分:1)

一种方法:

array.inject({}) {|r, (k, v)| r[k] &&= [*r[k], v]; r[k] ||= v; r }

但这很混乱。写出来,看起来像这样:

def to_hash_with_duplicates(arr)
  {}.tap do |r|
    arr.each do |k, v|
      r[k] &&= [*r[k], v]    # key already present, turn into array and add value
      r[k] ||= v             # key not present, simply store value
    end
  end
end

编辑:再考虑一下,@cary-swoveland使用块更新解决方案会更好,因为它可以正确处理nilfalse值。