creating a hash with elements from a different array of hashes in ruby

时间:2017-08-13 13:55:47

标签: arrays ruby hash

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?

5 个答案:

答案 0 :(得分:2)

您可以使用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"
# }

答案 1 :(得分:1)

这是另一种方法,效率低于@ 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"
}

答案 2 :(得分:1)

如果您想要一个类别中的每本书的列表,以及要在多个类别中显示的书籍,则可以在default_proc时附加initialize the Hash以将所有未知键设置为包含数组一个值。然后,您只需迭代库中的每本书,并将其标题添加到列表中,以用于它所在的每个类别:

# 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)

答案 3 :(得分:1)

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

      <?= $form->field($model, 'contact_id')->widget(Select2::className(), [
        'initValueText' => empty($model->contact_id) ? '' : $model->contact->contact_id . ' ' . $model->contact->fullname,
        'options' => [
            'class' => 'input-sm',
            'id' => 'contact_id',
            'placeholder' => '-- Search --',
            'disabled' => $disabled,
            'onchange' => new JsExpression("get_contact_info($(this).val())"),
        ],
        'pluginOptions' => [
            'allowClear' => true,
            'language' => [
                'errorLoading' => new JsExpression("function () { return 'Waiting for results...'; }"),
            ],
            'ajax' => [
                'url' => $fetch_url,
                'dataType' => 'json',
                'data' => new JsExpression('function(params) { return {q:params.term}; }'),
                'results' => new JsExpression('function(data,page) { return {results:data.results.text}; }'),
            ],
            'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
            'templateResult' => new JsExpression('function(contact) { return contact.text; }'),
            'templateSelection' => new JsExpression('function (contact) { return contact.text; }'),
        ],
    ]); ?>

答案 4 :(得分:0)

这就是我通常会这样做的方式,我认为这是最简单的方法,尽管上面提到的each_with_object技巧也很简洁。

new_hash = {}

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