Ruby - 如何用数组值反转哈希?
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"]
}
我为它写了一个很长的方法,但是对于大数据集,它非常慢。
有什么优雅的解决方案吗?
这是一种方式:
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
附录
对于 war 之前的 Ruby 版本(例如 1.8.7),只需先初始化散列并使用 each
而不是 each_with_object
(我们得到后者是 1.9。对于 1.8.7 我太年轻了,但我经常想知道没有它人们如何相处。)你只需要记住最后 return h
,因为 each
只是 return 它的接收者。
所以改成:
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"]}
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 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"]}
正在寻找适用于 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"]
}
我为它写了一个很长的方法,但是对于大数据集,它非常慢。
有什么优雅的解决方案吗?
这是一种方式:
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
附录
对于 war 之前的 Ruby 版本(例如 1.8.7),只需先初始化散列并使用 each
而不是 each_with_object
(我们得到后者是 1.9。对于 1.8.7 我太年轻了,但我经常想知道没有它人们如何相处。)你只需要记住最后 return h
,因为 each
只是 return 它的接收者。
所以改成:
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"]}
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 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"]}