0

In a bootcamp exercise, we were given this array:

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

and we need to make a new hash with the categories as the elements, and the titles as the values.

Was able to figure out how to make a category_hash, but I can't figure out how to append the titles to each category. Here is my code:

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

Can someone help me figure out this step?

1
  • You need to clarify (with an edit) what you mean by, "...with the categories as the elements...". Firstly, you mean "keys" rather than "elements". You will see that not all answers interpreted that the same way. To date, one answer assume "categories" are the values associated with the keys "categories" in the elements (hashes) of library; two answers assume "categories" are the strings included in one or more values of "categories" in the elements of library ("comedy", "fiction", "british", "morality", "society" and so on). I'm guessing you intended the latter interpretation. Commented Aug 13, 2017 at 20:41

5 Answers 5

2

You can use 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"
# }
Sign up to request clarification or add additional context in comments.

1 Comment

Suppose another hash were added to librar containing the key-value pair "categories" => [ "comedy", "fiction", "british"]. What then? See my comment on the question.
1

Here is another way to do it, which is less efficient than @AndreyDeineko's answer.

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

Result:

{
  ["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"
}

In case you wanted it to be distributed by the "category" attribute (which, of course, would be dependent on the order of the hash items in the array; I assume you meant later one has priority over an earlier one):

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

Result:

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

Comments

1

If you wanted a list of each book in a category, and a book to appear in multiple categories, you can attach a default_proc when you initialize the Hash to set all unknown keys to contain an array as a value. Then you simply iterate each book in the library and add it's title to the list for every category it's in:

# 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

Result:

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

There's also each_with_object so you could modify this to

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

which would return the category_hash and, to save that last line of code:

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)

Comments

1

This is a slight variant of other answers.

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

Comments

0

This is how I would normally do it and I think it's the simplest possible way, although the each_with_object trick seen above is neat too.

new_hash = {}

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.