【问题标题】:Ruby - Manipulate CSV data, insert into databaseRuby - 操作 CSV 数据,插入数据库
【发布时间】:2018-05-02 10:55:17
【问题描述】:

我正在使用需要 Ruby 的 CSV 类来导入属性数据行的 rake 任务,并且希望在将这些数据插入数据库之前对其进行操作。

CSV

PID,City,Address,Sold Date,Sold Price
100-200-300,Vancouver,510 1700 Nelson Street,01/01/2017,"$500,000 "
200-300-400,Vancouver,304 68 Smithe Street,02/02/2017,"600,000"

驻地表(为简洁起见缩短)

+-----+------+------+---------------+-------------+
| pid | city | unit | street_number | street_name |
+-----+------+------+---------------+-------------+
|     |      |      |               |             |
+-----+------+------+---------------+-------------+

Rake 任务(目前为止)

require 'csv'

desc 'Upload CSV data into database'
task residences: :environment do
  residences = Array.new
  counter    = 0
  csv_file   = "#{Rails.root}/public/spreadsheets/unformatted-addresses.csv"

  CSV.foreach(csv_file, headers: true, header_converters: :symbol, converters: :all, skip_blanks: true, encoding: 'UTF-8') do |row|

    #is this the right place to create the hash?
    residences << row.to_hash

    #is this the right way to format each cell?
    residences[counter][:pid]
    residences[counter][:city].downcase
    residences[counter][:address].downcase.split(" ")
    residences[counter][:sold_date]
    residences[counter][:sold_price].delete('$ ,').to_i

    Residence.create( #what to put here? )

    counter += 1
  end

  puts "Imported #{counter} rows."
end

我想要实现的是单独格式化单元格内容然后插入到适当的列中,例如地址格式应该是:

“单位”、“街道编号”、“街道名称”

非常感谢您对此的任何帮助!

【问题讨论】:

  • 我知道您想要实现什么,但是您的具体问题/问题是什么?我什至不知道您打算为此使用什么模型
  • 假设 Residence 模型具有您描述的字段,您可以执行类似 Residence.create(:unit => unit, :street_number => street_number, :street_name => street_name) 的操作,替换单元, street_name, street_number 与您的实际数据
  • 谢谢 Anton 和 Pierre,我已经更新了这个问题,希望能更加明确。该语言相对较新,因此请原谅含糊不清,但主要寻找正确的顺序:遍历行 > 格式化单元格 > 将地址拆分为多个部分 > 将记录插入数据库。
  • 提示:不要使用Array.new,只需使用[ ]。长格式版本适用于需要将参数传递给 new 的特殊情况。

标签: ruby-on-rails ruby database csv


【解决方案1】:

添加到我之前的答案,你应该能够做这样的事情:

require 'csv'

address_regex = /(^\d+[a-z]?)+\s+(\d+)+\s+(.*)/i

desc 'Upload CSV data into database'
task residences: :environment do
  counter    = 0
  csv_file   = "#{Rails.root}/public/spreadsheets/unformatted-addresses.csv"

  CSV.foreach(csv_file, headers: true, header_converters: :symbol, converters: :all, skip_blanks: true, encoding: 'UTF-8') do |row|

    address = address_regex.match(row[:address])

    Residence.create(
      pid:           row[:pid],
      city:          row[:city],
      unit:          address[1],
      street_number: address[2],
      street_name:   address[3]
    )

    counter += 1
  end

  puts "Imported #{counter} rows."
end

【讨论】:

  • 感谢@DivXZero 的回复,肯定更接近我想要实现的目标。我收到以下错误:NoMethodError: undefined method `[]' for nil:NilClass
  • 是 header_converters: :symbol 搞砸了我。应该是 row[:pid]。
【解决方案2】:

最终结果如下。

require 'csv'
require 'time'

namespace :csv do
  desc 'Upload CSV data into database'
  task residences: :environment do
    residences    = []
    counter       = 0
    csv_file      = "#{Rails.root}/public/spreadsheets/unformatted-addresses.csv"
    address_regex = /^(\d+[a-z]?)+\s+(\d+)+\s+(.+(?=\W))+\s+(.*)/i


    CSV.foreach(csv_file, headers: true, header_converters: :symbol, converters: :all, skip_blanks: true, encoding: 'UTF-8') do |row|
        address       = address_regex.match(row[:address])
        unit          = address[1]
        street_number = address[2]
        street_name   = address[3]
        street_type   = address[4]
        pid           = row[:pid].strip
        city          = row[:city].strip.downcase
        date          = Date.parse(row[:sold_date])
        sold_date     = date.strftime("%m-%d-%Y")
        sold_price    = row[:sold_price].strip.delete('$ ,').to_i

        puts "#{address}, #{pid}, #{city}, #{sold_date}, #{sold_price}"

        Residence.create(
          pid:           pid,
          city:          city,
          unit:          unit,
          street_number: street_number,
          street_name:   street_name,
          street_type:   street_type,
          sold_date:     sold_date,
          sold_price:    sold_price
        )

        counter += 1
    end

    puts "Imported #{counter} rows."
  end
end

【讨论】:

  • 您可能会在使用 split 方法时遇到问题,因为街道名称会有所不同,例如,“20 304 New York Street”将返回“York”作为 street_type。您可以使用这个更新的正则表达式,它将使用地址 [4] 为您提供街道类型,就像我的其他示例一样:/^(\d+[a-z]?)+\s+(\d+)+\s+(.+(?=\W))+\s+(.*)/i
  • 如果你想了解它在做什么,你可以在这里查看我是如何构建它的,点击底部的“解释”或“详细信息”:) regexr.com/3h7e6
【解决方案3】:

这应该适用于您正在尝试做的事情,假设每个地址都将有一个单位(它还将包括任何带有诸如“12A”之类的字符的单位:

address_regex = /(^\d+[a-z]?)+\s+(\d+)+\s+(.*)/i

matches = address_regex.match(residences[counter][:address])

unit          = matches[1]
street_number = matches[2]
street_name   = matches[3]

Codepad Example

请注意,这不是最有效的代码,我只是为了清楚起见

【讨论】:

    猜你喜欢
    • 2019-10-03
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 2017-06-05
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 2018-12-20
    相关资源
    最近更新 更多