Пользовательские конвертеры Ruby CSV для нескольких полей

Я импортирую файл CSV, который содержит поля, которые необходимо преобразовать следующим образом:

  • "True" (строка) -> true (логическое значение)
  • "False" (строка) -> false (логическое значение)
  • "% m /% d /% Y" (формат строки) -> объект Date
  • "%m/%d/%Y %I:%M:%S %p" (формат строки) -> объект DateTime

Преобразователи CSV по умолчанию не соответствуют полям Date и DateTime. Этот метод ниже, кажется, работает, но интересно, есть ли лучший способ, возможно, переопределить шаблон сопоставления, используемый конвертерами?

 require 'csv'
 require 'date'

 src = <<csv
 active,date_created,date_modified
 "True","03/12/2012","03/12/2012 2:14:23 PM"
 "False","01/25/2011","03/12/2013 3:14:27 AM"
 csv

 CSV::Converters[:my_converters] = lambda{|field| 
   begin 
     case field.to_s
       when "True"
         true
       when "False"
         false
       when /^\d{2}\/\d{2}\/\d{4}$/
         Date.strptime(field,"%m/%d/%Y")
       else
         DateTime.strptime(field,"%m/%d/%Y %I:%M:%S %p")
       end
   rescue ArgumentError
     field
   end
 }

 csv = CSV(src, :headers => true, :converters => [:my_converters])
 csv.each{|row| puts row}

правда,2012-03-12,2012-03-12T14:14:23+00:00

ложь,2011-01-25,2013-03-12T03:14:27+00:00

2 ответа

Решение

Это правильный способ сделать что-то, если конвертеров по умолчанию недостаточно. Мое единственное предложение состоит в том, чтобы разделить ваши конвертеры на разные лямбды, поскольку библиотека CSV уже предназначена для тестирования каждого поля с массивом конвертеров (что делает ваш case избыточный).

Но если это всего лишь быстрый одноразовый сценарий, то, что у вас есть, достаточно хорошо.

Преобразуйте поля соответственно имени поля (имени заголовка)

custom_converter = lambda { |value, field_info|
  case field_info.header
  when 'OrderUuid', 'Exchange', 'Type', 'OrderType'
    value.to_s
  when 'Quantity', 'Limit', 'CommissionPaid', 'Price'
    value.to_f
  when 'Opened', 'Closed'
    Time.zone.parse(value)
  else
    fail("Unknown field name #{field_info.inspect}=#{value}")
  end
}

CSV.parse(content, headers: :first_row, converters: [custom_converter]).map(&:to_h)
Другие вопросы по тегам