将数组的哈希合并为哈希数组

时间:2020-04-11 10:27:31

标签: ruby

所以,我有一个带有数组的哈希,就像这样:

{"name": ["John","Jane","Chris","Mary"], "surname": ["Doe","Doe","Smith","Martins"]}

我想将它们合并为一个哈希数组,结合相应的元素。

结果应该是这样的:

[{"name"=>"John", "surname"=>"Doe"}, {"name"=>"Jane", "surname"=>"Doe"}, {"name"=>"Chris", "surname"=>"Smith"}, {"name"=>"Mary", "surname"=>"Martins"}] 

有什么想法可以有效地做到这一点吗?


请注意,实际使用场景可能包含可变数量的哈希键。

5 个答案:

答案 0 :(得分:2)

尝试

h[:name].zip(h[:surname]).map do |name, surname|
  { 'name' => name, 'surname' => surname }
end

答案 1 :(得分:1)

我建议编写代码以允许任意数量的属性。这并不比假设有两个(:name:surname)困难,但是它提供了更大的灵活性,例如,将来可以更改属性的数量或命名:

def squish(h)
  keys = h.keys.map(&:to_s)
  h.values.transpose.map { |a| keys.zip(a).to_h }
end

h = { name:    ["John", "Jane", "Chris"],
      surname: ["Doe", "Doe", "Smith"],
      age:     [22, 34, 96]
    }    

squish(h)
  #=> [{"name"=>"John", "surname"=>"Doe", "age"=>22},
  #    {"name"=>"Jane", "surname"=>"Doe", "age"=>34},
  #    {"name"=>"Chris", "surname"=>"Smith", "age"=>96}] 

以上示例的步骤如下:

b = h.keys
  #=> [:name, :surname, :age] 
keys = b.map(&:to_s)
  #=> ["name", "surname", "age"] 
c = h.values
  #=> [["John", "Jane", "Chris"], ["Doe", "Doe", "Smith"], [22, 34, 96]] 
d = c.transpose
  #=> [["John", "Doe", 22], ["Jane", "Doe", 34], ["Chris", "Smith", 96]] 
d.map { |a| keys.zip(a).to_h }
  #=> [{"name"=>"John", "surname"=>"Doe", "age"=>22},
  #    {"name"=>"Jane", "surname"=>"Doe", "age"=>34},
  #    {"name"=>"Chris", "surname"=>"Smith", "age"=>96}] 

最后一步,将b的第一个值传递到map的块,并为块变量分配其值。

a = d.first
  #=> ["John", "Doe", 22] 
e = keys.zip(a)
  #=> [["name", "John"], ["surname", "Doe"], ["age", 22]] 
e.to_h
  #=> {"name"=>"John", "surname"=>"Doe", "age"=>22} 

其余计算类似。

答案 2 :(得分:1)

[h[:name], h[:surname]].transpose.map do |name, surname|
  { 'name' => name, 'surname' => surname }
end

答案 3 :(得分:1)

如果数据集确实很大,则可以考虑使用Enumerator::Lazy

这样,Ruby将不会在计算过程中创建中间数组。

这是@Ursus answer可以得到改进的方式:

h[:name]
  .lazy
  .zip(h[:surname])
  .map { |name, surname| { 'name' => name, 'surname' => surname } }
  .to_a

答案 4 :(得分:1)

以下情况的其他选择:

[..]实际使用场景可能包含可变数量的哈希键

h = {
      'name': ['John','Jane','Chris','Mary'],
      'surname': ['Doe','Doe','Smith','Martins'],
      'whathever': [1, 2, 3, 4, 5]
    }

您可以在一个衬里中将Object#thensplat operator一起使用:

h.values.then { |a, *b| a.zip *b }.map { |e| (h.keys.zip e).to_h }

#=> [{:name=>"John", :surname=>"Doe", :whathever=>1}, {:name=>"Jane", :surname=>"Doe", :whathever=>2}, {:name=>"Chris", :surname=>"Smith", :whathever=>3}, {:name=>"Mary", :surname=>"Martins", :whathever=>4}]

第一部分是这样的:

h.values.then { |a, *b| a.zip *b }
#=> [["John", "Doe", 1], ["Jane", "Doe", 2], ["Chris", "Smith", 3], ["Mary", "Martins", 4]]

最后一部分只是映射用原始键压缩每个元素的元素,然后调用Array#to_h转换为哈希。

在这里,我删除了呼叫.to_h以显示中间结果:

h.values.then { |a, *b| a.zip *b }.map { |e| h.keys.zip e }
#=> [[[:name, "John"], [:surname, "Doe"], [:whathever, 1]], [[:name, "Jane"], [:surname, "Doe"], [:whathever, 2]], [[:name, "Chris"], [:surname, "Smith"], [:whathever, 3]], [[:name, "Mary"], [:surname, "Martins"], [:whathever, 4]]]