哈希映射谜语

时间:2016-02-08 18:39:38

标签: ruby

在Ruby中,我该如何转换它:

{"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
 "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"},
 "3"=>{"id"=>3, "album"=>"album2", "track"=>"track1"},
 "4"=>{"id"=>4, "album"=>"album2", "track"=>"track2"}}

进入这个:

{"album1"=>
  {"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
   "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"}},
 "album2"=>
  {"3"=>{"id"=>3, "album"=>"album2", "track"=>"track1"},
   "4"=>{"id"=>4, "album"=>"album2", "track"=>"track2"}}}

以最有效的方式。

第一种是iTunes存储曲目信息的格式。最后一个是我需要在“专辑”级别处理曲目的格式。我一整天都在盯着这个,并且不擅长Ruby,已经失败了。感谢您关于hash kung-foo的教程。

修改

当我在等待主持人决定这是否合适时,我得到了一个解决方案:

album_tracks = {}
titles = []
tracks_hash.each do |album_id, album_hash|
  titles << album_hash["album"] if !titles.include? album_hash["album"]
end

titles.each do |title|
  tracks = {}
  tracks_hash.each do |album_id, album_hash|
    tracks[album_id] = album_hash if title == album_hash["album"]
  end
  albums_hash[title] = tracks
end

我猜测有一种更有效的策略涉及某种映射,不需要两次传递整个哈希?

3 个答案:

答案 0 :(得分:2)

您的输出可以通过对group_by的非常直接的调用来实现,然后进行一些转换以将结果转换为哈希值:

albums = {"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
          "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"},
          "3"=>{"id"=>3, "album"=>"album2", "track"=>"track1"},
          "4"=>{"id"=>4, "album"=>"album2", "track"=>"track2"}}

albums.group_by { |k,v| v['album'] }.map { |k,v| [k, v.to_h] }.to_h

# => {
#  "album1"=> {
#    "1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
#    "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"}
#   },
#  "album2"=>{
#    "3"=>{"id"=>3, "album"=>"album2", "track"=>"track1"},
#    "4"=>{"id"=>4, "album"=>"album2", "track"=>"track2"}
#  }
#}

关键是要了解Enumerable上哪些方法可用于将一个结构转换为另一个结构(即group_bymap),然后知道Ruby允许您自由地将数组转换为哈希值反之亦然。

第一个调用albums.group_by { |k,v| v['album'] }生成正确的外部哈希结构,但值的格式为[[key1, value1], [key2, value2], ...]。 Ruby允许您使用{key1: value1, key2: value2}将相同的结构转换回to_h哈希。

答案 1 :(得分:0)

这应该可以解决问题。

album_tracks = tracks_hash.each_with_object({}) do |(album_id, album_hash), album_tracks|
  album_tracks[album_hash['album']] ||= {}
  album_tracks[album_hash['album']][album_id] = album_hash
end

答案 2 :(得分:0)

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

h = { "1"=>{ "id"=>1, "album"=>"album1", "track"=>"track1" },
      "2"=>{ "id"=>2, "album"=>"album1", "track"=>"track2" },
      "3"=>{ "id"=>3, "album"=>"album2", "track"=>"track1" },
      "4"=>{ "id"=>4, "album"=>"album2", "track"=>"track2" } }

h.each_with_object({}) do |(k,v),g|
  g.update(v["album"]=>{ k=>v}) { |_,o,n| o.update(n) }
end
  #=> {"album1"=>{"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
  #               "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"}},
  #    "album2"=>{"3"=>{"id"=>3, "album"=>"album2", "track"=>"track1"},
  #               "4"=>{"id"=>4, "album"=>"album2", "track"=>"track2"}}}   

请注意update的参数

v["album"]=>{ k=>v}

是哈希

的简写
{ v["album"]=>{ k=>v} }

步骤:

enum = h.each_with_object({})
  #=> #<Enumerator: {"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
  #                  "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"},
  #                  "3"=>{"id"=>3, "album"=>"album2", "track"=>"track1"}, 
  #                  "4"=>{"id"=>4, "album"=>"album2", "track"=>"track2"}}:
  #                  each_with_object({})> 

enum的第一个元素传递给块,并使用分解分配块变量:

 (k,v),g = enum.next
   #=> [["1", {"id"=>1, "album"=>"album1", "track"=>"track1"}], {}] 
 k #=> "1" 
 v #=> {"id"=>1, "album"=>"album1", "track"=>"track1"}
 g #=> {}

然后执行块计算:

 g.update(v["album"]=>{ k=>v }) { |_,o,n| o.update(n) }
   #=> {}.update("album1"=>{ "1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"})
   #      { |_,o,n| o.update(n) }
   #=> {"album1"=>{ "1"=>{ "id"=>1, "album"=>"album1", "track"=>"track1"}}} 

合并了两个哈希,{}

{ "album1"=>{ "1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"} }

没有共同的键,因此该块不用于计算任何值。

然后将enum的下一个元素传递给块:

(k,v),g = enum.next
  #=> [["2", {"id"=>2, "album"=>"album1", "track"=>"track2"}], {}]
k #=> "2" 
v #=> {"id"=>2, "album"=>"album1", "track"=>"track2"} 
g #=> {"album1"=>{"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"}}} 

现在更新计算

g.update(v["album"]=>{ k=>v }) { |_,o,n| o.update(n) }
  #=> g.update("album1"=>{ "2"=> {"id"=>2, "album"=>"album1", "track"=>"track2"})  

我们正在构建的哈希,

 g #=> { "album1"=>{"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1" } } }

并且哈希被合并,

 { "album1"=>{ "2"=> {"id"=>2, "album"=>"album1", "track"=>"track2"} } }

都有密钥"album1",因此该块用于确定该密钥的值:

在块中

 { |_,o,n| o.update(n) }

我们有

 o #=> { "1"=>{"id"=>1, "album"=>"album1", "track"=>"track1" } }
 n #=> { "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2" } }

所以

 { "1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"} }.
   update({ "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"} })
   #=> { "1"=>{"id"=>1, "album"=>"album1", "track"=>"track1" },
   #     "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"} } 

哈希g现在是

g #=> {"album1"=>{"1"=>{"id"=>1, "album"=>"album1", "track"=>"track1"},
  #               "2"=>{"id"=>2, "album"=>"album1", "track"=>"track2"}}} 

其余的计算方法类似。