繁体   English   中英

Ruby-如何使用数组值反转Hash?

[英]Ruby - How to invert a Hash with an array values?

寻找适用于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"] 
}

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

有什么优雅的解决方案吗?

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"]}

这是一种方法:

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的元素:

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 (我们将后者的值each_with_object 1.9。对于1.8.7来说我还太年轻,但是我经常想知道人们是如何得到的您只需要记住最后要返回h ,因为each h都只返回其接收者。

因此将其更改为:

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中执行以下操作:

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"]}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM