【问题标题】:Ruby - How to invert a Hash with an array values?Ruby - 如何用数组值反转哈希?
【发布时间】:2015-04-23 16:20:35
【问题描述】:

寻找适用于 Ruby 1.8.7 的答案:

例如,假设我有一个这样的哈希:

{"Book Y"=>["author B", "author C"], "Book X"=>["author A", "author B", "author C"]}

我想得到这个:

{ 
    "author A" => ["Book X"],
    "author B" => ["Book Y", "Book X"],
    "author C" => ["Book Y", "Book X"] 
}

我为它写了一个很长的方法,但是对于大型数据集,它超级慢。

任何优雅的解决方案?

【问题讨论】:

  • 显示超慢方法。
  • Ruby 1.8.4 几乎在ten years ago 发布。您应该考虑升级到较新的版本。很难找到仍然使用这个过时版本的 gem。
  • 已经更新了问题,提到它适用于 Ruby 1.8.7。无论如何,对这个问题有效的两个答案将是 spickermann 和 Rustam A. Gasanov(下)。其余提供的答案(Cory 和sawa)适用于较新版本的 Ruby。谢谢大家!
  • Ruby 1.8.7 已经七岁了...

标签: ruby arrays hash ruby-1.8


【解决方案1】:

这是一种方式:

g = {"Book Y"=>["author B", "author C"],
     "Book X"=>["author A", "author B", "author C"]}

g.each_with_object({}) do |(book,authors),h|
  authors.each { |author| (h[author] ||= []) << book }
end
  #=> {"author B"=>["Book Y", "Book X"],
  #    "author C"=>["Book Y", "Book X"],
  #    "author A"=>["Book X"]} 

步骤:

enum = g.each_with_object({})
  #=> #<Enumerator: {"Book Y"=>["author B", "author C"],
  #   "Book X"=>["author A", "author B", "author C"]}:each_with_object({})> 

我们可以看到enum的元素,通过将其转换为数组,它将传递到块中:

enum.to_a
  #=> [[["Book Y", ["author B", "author C"]], {}],
  #    [["Book X", ["author A", "author B", "author C"]], {}]]

enum 的第一个元素传递给块并分配给块变量是:

(book,authors),h = enum.next
  #=> [["Book Y", ["author B", "author C"]], {}] 
book
  #=> "Book Y" 
authors
  #=> ["author B", "author C"] 
h
  #=> {} 

enum1 = authors.each
  #=> #<Enumerator: ["author B", "author C"]:each>
author = enum1.next
  #=> "author B"
(h[author] ||= []) << book
  #=> (h["author B"] ||= []) << "Book Y"
  #=> (h["author B"] = h["author B"] || []) << "Book Y"
  #=> (h["author B"] = nil || []) << "Book Y"
  #=> h["author B"] = ["Book Y"]
  #=> ["Book Y"]
h #=> {"author B"=>["Book Y"]} 

下一步:

author = enum1.next
  #=> "author C" 
(h[author] ||= []) << book
h #=> {"author B"=>["Book Y", "Book Y"], "author C"=>["Book Y"]} 

已完成"Book X"

(book,authors),h = enum.next
  #=> [["Book X", ["author A", "author B", "author C"]],
  #    {"author B"=>["Book Y", "Book Y"], "author C"=>["Book Y"]}]
book
  #=> "Book X" 
authors
  #=> ["author A", "author B", "author C"] 
h
  #=> {"author B"=>["Book Y", "Book Y"], "author C"=>["Book Y"]} 

我们现在重复与 "Book X" 相同的计算。唯一不同的是,当我们遇到:

(h[author] ||= []) << book

相当于

(h[author] = h[author] || []) << book

在大多数情况下,等号右边的h[author]不会是nil(例如,它可能是["Book X"],在这种情况下,上面的表达式简化为:

h[author] << book

附录

对于战前的 Ruby 版本(例如 1.8.7),只需先初始化哈希并使用 each 而不是 each_with_object(我们在 1.9 中得到了后者。我对 1.8.7 来说太年轻了,但我经常想知道没有它人们如何相处。)你只需要记住在最后返回h,因为each只是返回它的接收者。

所以改成:

h = {}
g.each do |book,authors|
  authors.each { |author| (h[author] ||= []) << book }
end
h
  #=> {"author B"=>["Book Y", "Book X"],
  #    "author C"=>["Book Y", "Book X"],
  #    "author A"=>["Book X"]} 

【讨论】:

  • 有没有适用于 Ruby 1.8.4 的方法?因为“flat_map”似乎不起作用。
  • 一般情况下你可以在flatten的好地方处理这个问题。如果您在这方面仍然需要帮助,我会在写完说明后查看。
  • 嗯,我以前没有使用过 flatten 方法。它看起来像它的数组而不是哈希。如果我使用flatten,最终的代码会是什么样子?
  • 我很抱歉消失了几分钟。我误读了这个问题(我知道这很难做到),所以必须修正我的答案。
  • 哦,对了,您使用的是旧版本的 Ruby。我们在 Ruby 1.9 中获得了each_with_object。您可以改用Enumerable#reduce(又名inject)。我会编辑给你看。顺便说一句,你有什么理由不能使用更现代的 Ruby 版本吗?
【解决方案2】:
h = {"Book Y"=>["author B", "author C"], "Book X"=>["author A", "author B", "author C"]}

p h.inject(Hash.new([])) { |memo,(key,values)|
  values.each { |value| memo[value] += [key] }
  memo
}
# => {"author B"=>["Book Y", "Book X"], "author C"=>["Book Y", "Book X"], "author A"=>["Book X"]}

【讨论】:

  • 这还是有用的,谢谢! Ruby 缺少一个好的多映射(我可以找到),因此反转数组的哈希是一种解决值查找的 hack 但有效的方法
【解决方案3】:

我会在 Ruby 1.8 中做这样的事情:

hash = {"Book Y"=>["author B", "author C"], "Book X"=>["author A", "author B", "author C"]}

library = Hash.new { |h, k| h[k] = [] }

hash.each do |book, authors|
  authors.each { |author| library[author] << book }
end

puts library 
#=> {"author B"=>["Book Y", "Book X"], "author C"=>["Book Y", "Book X"], "author A"=>["Book X"]}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-07
    • 2020-03-24
    • 1970-01-01
    • 2015-11-27
    相关资源
    最近更新 更多