Ruby:如何将该数组转换为该哈希?

时间:2018-06-27 23:39:43

标签: arrays ruby hash

我有一个数组数组。数组中的每个项目都包含三个字符串:一个腿数,一个动物和一个声音。

a = [ ['4', 'dog', 'woof'] , ['4', 'cow', 'moo'], ['2', 'human', 'yo'] , ['2', 'yeti', 'wrarghh'] ]

我想将数组变成这个哈希:

{ 
  '2' => [ { 'human' => 'yo' }, { 'yeti' => 'wrarghh'} ],
  '4' => [ { 'dog' => 'woof' }, { 'cow' => 'moo'} ]
}

我以为减少将是必经之路,但我运气不佳。我当前的刺痛看起来像:

a.reduce({}) do |acc, item|
             acc[item.first] = [] unless acc.key? item.first
             acc[item.first] << { item[1] => item[2] }
           end

但是出现错误:

NoMethodError: undefined method `key?' for [{"dog"=>"woof"}]:Array

实现此目标的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

a.each_with_object({}) { |(kout, kin, val), h| (h[kout] ||= []) << { kin => val } }
  #=> {"4"=>[{"dog"=>"woof"}, {"cow"=>"moo"}], "2"=>[{"man"=>"yo"}, {"yeti"=>"wrarghh"}]}

我们有

enum = a.each_with_object({})
  #=> #<Enumerator: [["4", "dog", "woof"], ["4", "cow", "moo"], ["2", "man", "yo"],
  #                  ["2", "yeti", "wrarghh"]]:each_with_object({})>

第一个值由该枚举器生成并传递给块,并且为块变量分配了值:

(kout, kin, val), h = enum.next
  #=> [["4", "dog", "woof"], {}]

其分解如下。

kout
  #=> "4"
kin
  #=> "dog"
val
  #=> "woof"
h #=> {}

因此,块计算是

(h[kout] ||= []) << { kin => val }
  #=> (h[kout] = h[kout] || []) << { "dog" => "wolf" }
  #=> (h["4"] = h["4"] || []) << { "dog" => "wolf" }
  #=> (h["4"] = nil ||= []) << { "dog" => "wolf" }
  #=> (h["4"] = []) << { "dog" => "wolf" }
  #=> [] << { "dog" => "wolf" }
  #=> [{ "dog" => "wolf" }]

h["4"] || [] #=> [],因为h没有密钥"4",因此没有h["4"] #=> nil

下一个enum的值传递到该块并重复计算。

(kout, kin, val), h = enum.next
  #=> [["4", "cow", "moo"], {"4"=>[{"dog"=>"woof"}]}]
kout
  #=> "4"
kin
  #=> "cow"
val
  #=> "moo"
h #=> {"4"=>[{"dog"=>"woof"}]}

(h[kout] ||= []) << { kin => val }
  #=> (h[kout] = h[kout] || []) << { "cow" => "moo" }
  #=> (h["4"] = h["4"] || []) << { "cow" => "moo" }
  #=> (h["4"] = [{"dog"=>"woof"}] ||= []) << { "cow" => "moo" }
  #=> (h["4"] = [{"dog"=>"woof"}]) << { "cow" => "moo" }
  #=> [{"dog"=>"woof"}] << { "cow" => "moo" }
  #=> [{ "dog" => "wolf" }, { "cow" => "moo" }]

这次h["4"] || [] #=> [{ "dog" => "wolf" }]是因为h现在有一个具有真实值("4")的密钥[{ "dog" => "wolf" }]

其余计算类似。

答案 1 :(得分:2)

您的方式可行,但是对于reduce,块的返回值(即最后一行)成为acc的下一个值,因此您需要做的只是更改是:

a.reduce({}) do |acc, item|
  acc[item.first] = [] unless acc.key? item.first
  acc[item.first] << { item[1] => item[2] }

  acc # just add this line
end

由于Array#<<的返回值是数组本身,因此第二次迭代将acc作为第一个元素的数组。当然,有很多方法可以做到这一点,有些方法可以说是更清洁的方法,但是当发现某些我认为不可行的方法出了错时,我发现知道自己出了错是很有用的。