哈希到数组转换

时间:2011-09-22 10:11:53

标签: ruby arrays hash

我需要这个哈希

{"peter" => ["apple", "orange", "mango"], "sandra" => ["flowers", "bike"]}

转换为此数组:

[["peter", "apple"], ["peter", "orange"], ["peter", "mango"], ["sandra", "flowers"], ["sandra", "bike"]]

现在我有了这个解决方案

my_hash.inject([]){|ar, (k,v)| ar << v.map{|c| [k,c]}}.flatten(1)

但我相信这里有ziptranspose magick更优雅的解决方案:)

3 个答案:

答案 0 :(得分:5)

您对Enumerable#inject解决方案持怀疑态度。在Ruby中,inject/reduce有点滥用,我们必须小心并选择正确的抽象(map,select,zip,flatten ......),如果它们适合手头的问题。在这种情况下:

h = {"peter" => ["apple", "orange", "mango"], "sandra" => ["flowers", "bike"]}
h.map { |k, vs| vs.map { |v| [k, v] } }.flatten(1)
#=> [["peter", "apple"], ["peter", "orange"], ["peter", "mango"], ["sandra", "flowers"], ["sandra", "bike"]]

但是如果你想使用Enumerable#zip,不要让任何人阻止你; - )

h.map { |k, vs| [k].cycle(vs.size).zip(vs) }.flatten(1)

正如@steenslag所说:

h.map { |k, vs| [k].product(vs) }.flatten(1)

所以最后我们可以写:

h.flat_map { |k, vs| [k].product(vs) }

答案 1 :(得分:2)

h.inject([]){|a,(k,vs)| a+vs.map {|v| [k,v]}}

您也可以使用此版本

h.inject([]){|a,(k,vs)| a+=vs.map {|v| [k,v]}}

哪个是最有效的,因为它使用相同的列表而不是在每次迭代时创建一个新列表。但是(对我来说)使用inject并修改变量是错误的。 each版本也会这样做。

a = []; h.each {|k,vs| a+=vs.map {|v| [k,v]}}

它略短且具有表现力。

答案 2 :(得分:1)

使用zip

hash.inject([]){ |ar, (k,v)| ar << ([k]*v.size).zip(v) }

使用transpose的合理解决方案:

[
  hash.keys.map{|k| [k]*hash[k].size }.flatten,
  hash.keys.map{|k| hash[k] }.flatten
].transpose

考虑到:

  1. hash.keys应该在两种情况下以相同的顺序返回密钥,因此除非您确定,否则不要将其用于其他语言。
  2. 我会选择第一个选项。