使用 Ruby 中不同哈希数组中的元素创建哈希



在训练营练习中,我们得到了这个数组:

library = [
{
"title" => "Hitchhiker's Guide", 
"Author" => "Douglas Adams", 
"categories" => [ "comedy", "fiction", "british"]
},
{
"title" => "Pride and Prejudice", 
"Author" => "Jane Austen", 
"categories" => [ "morality", "fiction", "society", "british"]
},
{
"title" => "Search Inside Yourself", 
"Author" => "Chade-Meng Tan", 
"categories" => [ "self improvement", "non-fiction", "mindfulness", "business"]
},
{
"title" => "Benjamin Franklin: An American Life", 
"Author" => "Walter Isaacson", 
"categories" => [ "non-fiction", "history", "founding fathers"]
},
{
"title" => "Glengarry Glen Ross", 
"Author" => "David Mamet", 
"categories" => [ "play", "fiction", "drama"]
}
]

我们需要创建一个新的哈希值,将类别作为元素,将标题作为值。

能够弄清楚如何制作category_hash,但我无法弄清楚如何将标题附加到每个类别。这是我的代码:

category_hash = {}
sub_category = []
library.each do |book|
book["categories"].each do |category|
sub_category << category
end
sub_category.each do |index|
category_hash[index] = "I'm not sure how to append the titles here"
end
end
p category_hash

有人可以帮我弄清楚这一步吗?

您可以使用Enumerable#each_with_object

library.each_with_object({}) do |hash, result|
result[hash['categories']] = hash['title']
end
# {
#   ["comedy", "fiction", "british"]=>"Hitchhiker's Guide",
#   ["morality", "fiction", "society", "british"]=>"Pride and Prejudice",
#   ["self improvement", "non-fiction", "mindfulness", "business"]=>"Search Inside Yourself",
#   ["non-fiction", "history", "founding fathers"]=>"Benjamin Franklin: An American Life",
#   ["play", "fiction", "drama"]=>"Glengarry Glen Ross"
# }

这是另一种方法,它比@AndreyDeineko的答案效率低。

library.map{|h| h.values_at("categories", "title")}.to_h

结果:

{
["comedy", "fiction", "british"]=>"Hitchhiker's Guide", 
["morality", "fiction", "society", "british"]=>"Pride and Prejudice",
["self improvement", "non-fiction", "mindfulness", "business"]=>"Search Inside Yourself",
["non-fiction", "history", "founding fathers"]=>"Benjamin Franklin: An American Life",
["play", "fiction", "drama"]=>"Glengarry Glen Ross"
}

如果您希望它按"category"属性分布(当然,这将取决于数组中哈希项的顺序;我假设你的意思是后来的优先于较早的(:

library.each_with_object({}) do
|h, result| h["categories"].each_with_object(result) do
|k, result| result[k] = h["title"]
end
end

结果:

{
"comedy"=>"Hitchhiker's Guide",
"fiction"=>"Glengarry Glen Ross",
"british"=>"Pride and Prejudice",
"morality"=>"Pride and Prejudice",
"society"=>"Pride and Prejudice",
"self improvement"=>"Search Inside Yourself",
"non-fiction"=>"Benjamin Franklin: An American Life", 
"mindfulness"=>"Search Inside Yourself",
"business"=>"Search Inside Yourself",
"history"=>"Benjamin Franklin: An American Life",
"founding fathers"=>"Benjamin Franklin: An American Life",
"play"=>"Glengarry Glen Ross",
"drama"=>"Glengarry Glen Ross"
}

如果您希望一个类别中每本书的列表,以及一本书出现在多个类别中,则可以在初始化 Hash 时附加一个default_proc,以将所有未知键设置为包含数组作为值。然后,您只需迭代图书馆中的每本书,并将其标题添加到其所在每个类别的列表中:

# Pass the category hash a default proc, which will be called
# whenever a key it doesn't have is accessed. So, when you
# encounter a brand new category, it'll already be set up with
# an array
category_hash = Hash.new { |hash, key| hash[key] = [] }
library.each do |book|
book['categories'].each do |category|
# since we passed the default, no worrying, an array will be
# there and we can just add our value into it
category_hash[category] << book['title']
end
end
puts category_hash

结果:

{
"comedy"=>["Hitchhiker's Guide"],
"fiction"=>["Hitchhiker's Guide", "Pride and Prejudice", "Glengarry Glen Ross"],
"british"=>["Hitchhiker's Guide", "Pride and Prejudice"],
"morality"=>["Pride and Prejudice"],
"society"=>["Pride and Prejudice"],
"self improvement"=>["Search Inside Yourself"],
"non-fiction"=>["Search Inside Yourself", "Benjamin Franklin: An American Life"],
"mindfulness"=>["Search Inside Yourself"],
"business"=>["Search Inside Yourself"],
"history"=>["Benjamin Franklin: An American Life"],
"founding fathers"=>["Benjamin Franklin: An American Life"],
"play"=>["Glengarry Glen Ross"],
"drama"=>["Glengarry Glen Ross"]
}

还有each_with_object,因此您可以将其修改为

result = library.each.with_object(Hash.new { |hash, key| hash[key] = [] }) do |book, category_hash|
book['categories'].each do |category|
# since we passed the default, no worrying, an array will be
# there and we can just add our value into it
category_hash[category] << book['title']
end
end
puts result

这将返回category_hash,并保存最后一行代码:

puts(library.each.with_object(Hash.new { |hash, key| hash[key] = [] }) do |book, category_hash|
book['categories'].each do |category|
# since we passed the default, no worrying, an array will be
# there and we can just add our value into it
category_hash[category] << book['title']
end
end)

这是其他答案的轻微变体。

library = [
{title: "Hitch Guide", by: "Doug", cats: ["comedy", "fiction", "british"]},
{title: "P and P",     by: "Jane", cats: ["morality", "fiction", "british"]},
{title: "Searchin'",   by: "Chad", cats: ["non-fiction", "morality", "gps"]},
{title: "Crazy Ben",   by: "Walt", cats: ["non-fiction", "comedy", "dads"]}
]
# => [{:title=>"Hitch Guide", :by=>"Doug", :cats=>["comedy", "fiction", "british"]},
#     {:title=>"P and P", :by=>"Jane", :cats=>["morality", "fiction", "british"]},
#     {:title=>"Searchin'", :by=>"Chad", :cats=>["non-fiction", "morality", "gps"]},
#     {:title=>"Crazy Ben", :by=>"Walt", :cats=>["non-fiction", "comedy", "dads"]}]
library.each_with_object({}) do |g,h|
title = g[:title]
g[:cats].each { |c| (h[c] ||= []) << title }
end
#=> {"comedy"=>["Hitch Guide", "Crazy Ben"],
#    "fiction"=>["Hitch Guide", "P and P"],
#    "british"=>["Hitch Guide", "P and P"],
#    "morality"=>["P and P", "Searchin'"],
#    "non-fiction"=>["Searchin'", "Crazy Ben"],
#    "gps"=>["Searchin'"],
#    "dads"=>["Crazy Ben"]}

这就是我通常的做法,我认为这是最简单的方法,尽管上面看到的each_with_object技巧也很巧妙。

new_hash = {}
library.each do |hash| 
hash["categories"].each do |category|
new_hash[category] ||= []
new_hash[category] << hash["title"]
end
end

最新更新