Rails / Ruby: превращение результата запроса в многоуровневый хеш - PullRequest
0 голосов
/ 04 июля 2018

У меня есть запрос *, который приводит к следующему:

#<ActiveRecord::Relation [
    #<BookRank id: 2, book_id: 2, list_edition_id: 1, rank_world: 5, rank_europe: 1>,
    #<BookRank id: 3, book_id: 1, list_edition_id: 1, rank_world: 6, rank_europe: 2>,
    #<BookRank id: 8, book_id: 2, list_edition_id: 3, rank_world: 1, rank_europe: 1>,
    #<BookRank id: 9, book_id: 1, list_edition_id: 3, rank_world: 2, rank_europe: 2
]>

То, что я пытаюсь получить, это хеш-код:

{
    book_id => {
        list_edition_id => {
            "rank_world" => value,
            "rank_europe" => value
        }
    }
}

(Вишня сверху должна была бы упорядочить хеш по значению rank_world для самого низкого list_edition_id, но, возможно, это может быть слишком сложно.)

ranks_relation.group_by(&:book_id) дает мне хэш, где book_ids являются ключами, но затем данные рангов все еще находятся в массивах:

{
    2 => [
            #<BookRank id: 2, book_id: 2, list_edition_id: 1, rank_world: 5, rank_europe: 1>,
            #<BookRank id: 8, book_id: 2, list_edition_id: 3, rank_world: 1, rank_europe: 1>
    ],
    1 => [
            #<BookRank id: 3, book_id: 1, list_edition_id: 1, rank_world: 6, rank_europe: 2>
            #<BookRank id: 9, book_id: 1, list_edition_id: 3, rank_world: 2, rank_europe: 2>
    ]
}

Как мне поступить?

* РЕДАКТИРОВАТЬ: Это структура модели и запрос. Другой пользователь спросил об этом:

class Book < ActiveRecord::Base
  has_many :book_ranks, dependent: :destroy
end

class List < ActiveRecord::Base
  has_many :list_editions, dependent: :destroy
end

class ListEdition < ActiveRecord::Base
  belongs_to :list
  has_many :book_ranks, dependent: :destroy
end

class BookRank < ActiveRecord::Base
  belongs_to :book
  belongs_to :list_edition

  has_one :list, through: :list_edition
end

Для запроса я уже использую два массива с соответствующими идентификаторами для Book и ListEdition:

BookRank.where(:book_id => book_ids, :list_edition_id => list_edition_ids)

Ответы [ 3 ]

0 голосов
/ 04 июля 2018
ranks_relation.
  group_by(&:book_id).
  map do |id, books|
    [id, books.map do |book|
           [
             book.list_edition_id,
             {
               "rank_world" => book.rank_world,
               "rank_europe" => book.rank_europe
             }
           ]
         end.sort_by { |_, hash| hash["rank_world"] }.to_h
    ]
  end.to_h
0 голосов
/ 04 июля 2018

Попробуйте это

record = your_record

hash = {}

record.each do |record|
  hash[record.book_id] ||= {}
  hash[record.book_id][record.list_edition_id] = {
    'rank_world' => record.rank_world,
    'rank_europe' => record.rank_europe
  }
end
# hash will then be {2=>{1=>{"rank_world"=>5, "rank_europe"=>1}, 3=>{"rank_world"=>1, "rank_europe"=>1}}, 1=>{1=>{"rank_world"=>6, "rank_europe"=>2}, 3=>{"rank_world"=>2, "rank_europe"=>2}}}

Это будет повторять запись только один раз.

0 голосов
/ 04 июля 2018

Привет, @gibihmruby (хорошее имя, кстати),

, так как вы попросили в комментариях более конкретное описание моего некрасивого подхода с использованием только group_by и друзей, вот мое предложение:

rel.group_by(&:book_id).map do |k, v| 
  [k, v.group_by(&:list_edition_id)]
end.to_h

даст структуру типа

{2=>
  {1=>
    [#<struct BookRank
      id=2,
      book_id=2,
      list_edition_id=1,
      rank_world=5,
      rank_europe=1>],
   3=>
    [#<struct BookRank
      id=8,
      book_id=2,
      list_edition_id=3,
      rank_world=1,
      rank_europe=1>]},
 1=>
  {1=>
    [#<struct BookRank
      id=3,
      book_id=1,
      list_edition_id=1,
      rank_world=6,
      rank_europe=2>],
   3=>
    [#<struct BookRank
      id=9,
      book_id=1,
      list_edition_id=3,
      rank_world=2,
      rank_europe=2>]}}

Тогда вам придется сопоставить самый внутренний объект с атрибутами, которые вы хотите. Если вы уверены, что комбинация book_id и list_edition_id уникальна, вы можете избавиться от переноса массива и затем сопоставить его с необходимыми атрибутами. Вы можете использовать slice для ActiveRecord объектов. Тогда отображение будет

rel.group_by(&:book_id).map do |book_id, grouped_by_book_id| 
  [
    book_id, 
    grouped_by_book_id.group_by(&:list_edition_id).map do |list_ed_id, grouped|
      [list_ed_id, grouped.first.slice(:rank_world, :rank_europe)]
    end.to_h
  ]
end.to_h

Поскольку я не создавал модель, а просто использовал структуры (как вы можете видеть в моем примере выше), я не проверял последний бит сам. Но это должно работать так, пожалуйста, прокомментируйте, если вы нашли ошибку или у вас есть дополнительные вопросы. Я все еще надеюсь, что кто-то найдет лучшее решение, потому что я сам слишком часто искал его сейчас.

Приветствия:)

редактировать: незначительные исправления

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...