Создание нескольких записей в ruby ​​из хэша массивов - PullRequest
0 голосов
/ 27 августа 2018

У меня есть ответ, состоящий из хэша массивов внутри хеша.Мне нужно создать три записи (см. Ниже) в ruby ​​/ rails.

{
    "first_name" => [John, Tom, Michael],
    "last_name" => [Smith, Watts, Pit],
    "email" => [John.smith@gmail.com, tom.watts@gmail.com,  mike.pit@gmail.com]
}

id  || first_name   || last_name  || email
----------------------------------------------
1   || John         ||  Smith     || john.smith@gmail.com
2   || Tom          ||  Watts     || tom.watts@gmail.com
3   || Michael      ||  Pit       || mike.pit@gmail.com

Мне трудно понять, как к этому подойти.Любая помощь будет очень признательна.

Пока у меня есть:

response.keys.each do |field_name|
  response[field_name].each do |value|
     puts "#{field_name} => #{value}"
     User.create!(
       first_name: value['first_name'],
       last_name: value['last_name'],
       email:  value['email']
     )
  end
end

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Если вам гарантирована, что длина массивов одинакова, а порядок не меняется, вы можете сделать что-то вроде:

data = {
  first_name: ["John", "Tom", "Michael"],
  last_name: ["Smith", "Watts","Pit"],
  email: ["John.smith@gmail.com", "tom.watts@gmail.com",  "mike.pit@gmail.com"]
}

0.upto(data[:first_name].length-1) do |i|
  record = {
    first_name: data[:first_name][i],
    last_name: data[:last_name][i],
    email: data[:email][i]
  }
  User.create!(record)
end

Вы также можете сойти с ума с помощью .zip и.each_slice, хотя я думаю, что вышесказанное является более простым и понятным.

maxlen = data[:first_name].length
first_name_fields = Array.new(maxlen, 'first_name')
last_name_fields = Array.new(maxlen, 'last_name')
email_fields = Array.new(maxlen, 'email')
records = first_name_fields.zip(data[:first_name], last_name_fields, data[:last_name], email_fields, data[:email])
records.map!{|x| x.each_slice(2).to_a.to_h}
records.each{|record| User.create!(record)}
0 голосов
/ 27 августа 2018

Так же просто, как это:

response.values.transpose.each { |record|
  User.create!(data.keys.zip(record).to_h)
}

response.values даст нам массив массивов (массив значений полей для каждого поля), а transpose перевернет его так, что у нас есть массивзаписей.Затем для каждой записи просто добавьте имена полей, hashify и передайте #create!.

0 голосов
/ 27 августа 2018

Как насчет подхода, подобного -

def create_records_from_responses!(responses)
  items = responses.values.first.length
  fields = responses.keys
  users = []

  # Don't process an empty set
  return if items.zero?

  # Wrap creation of User objects in a transaction
  # If one fails, it rolls back all created records
  # As per transaction documentation, you still have to manually
  # handle the propagated error
  # See: https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
  # In this case we named the method with a `!` to indicate that it might raise
  # an error. Whatever is calling this should rescue and handle the error

  ActiveRecord::Base.transaction do
    (0..items).each do |i|
      attributes = {}
      fields.each { |field| attributes[field] = responses[field][i] }

      users << User.create!(attributes)
    end
  end

  users

Это делает 2 предположения -

  1. Ваш хэш responses сформирован правильно.Если для каждой клавиши есть несовпадающие массивы, то все может пойти не так.Вам следует предварительно проверить ее форму

  2. Количество создаваемых вами пользователей невелико.Как вы можете видеть, он восстанавливает attributes в каждом цикле.Это не большая проблема, если это 10-20 записей, но подумайте об оптимизации, если вы обрабатываете больше.

...