【问题标题】:Creating multiple records in ruby from a hash of arrays从数组的哈希中创建多条记录
【发布时间】:2018-08-27 02:41:26
【问题描述】:

我有一个由散列中的数组散列组成的响应。我需要在 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

【问题讨论】:

  • hash of arrays within a hash 有点混乱。总是和你的例子一样吗?
  • 是的,一直都是。

标签: ruby-on-rails ruby rails-activerecord


【解决方案1】:

这样的方法怎么样 -

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

这有两个假设 -

  1. 您的responses 哈希格式正确。如果每个键都有不匹配的数组,那么事情可能会出错。你应该事先验证它的形式

  2. 您创建的用户数量并不多。如您所见,它在每个循环中重建 attributes。如果是 10 到 20 条记录,这并不是什么大问题,但如果您要处理更多记录,请考虑对其进行优化。

【讨论】:

    【解决方案2】:

    只要保证数组的长度相同,顺序不变,你可以这样做:

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

    【讨论】:

      【解决方案3】:

      就这么简单:

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

      response.values 会给我们一个数组数组(每个字段的字段值数组),transpose 会翻转它,所以我们有记录数组。然后对于每条记录,只需添加字段名称,哈希化并传递给#create!

      【讨论】:

      • 我喜欢这个答案的简单性。不幸的是,我的一些字段名称与哈希键不直接匹配,所以我需要手动关联它们。
      猜你喜欢
      • 2011-04-20
      • 2014-09-18
      • 1970-01-01
      • 2011-05-08
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多