Ruby: если столбец существует, используйте его, если не добавляете в другой столбец
Я анализирую CSV и пытаюсь различить столбцы в Model
и "виртуальные" столбцы, которые будут добавлены в JSONB :data
колонка. Пока у меня есть это:
rows = SmarterCSV.process(csv.path)
rows.each do |row|
row.select! { |x| Model.attribute_method?(x) } # this ignores non-matches
Model.create(row)
end
Это удаляет столбцы из строки CSV, которые не совпадают с Model
, Вместо этого я хочу добавить данные всех этих столбцов в Model
называется :data
, Как я могу это сделать?
редактировать
Примерно так до select!
может быть?
row[:data] = row.select { |x| !Model.attribute_method?(x) }
3 ответа
Есть несколько способов сделать это. Один особенно простой способ заключается в Hash#slice!
из расширений ActiveSupport Rails, который работает как Array#slice!
и возвращает хэш с теми ключами, которые не были указаны в его аргументах, при сохранении ключей, которые были даны:
rows = SmarterCSV.process(csv.path)
attrs = Model.attribute_names.map(&:to_sym)
rows.each do |row|
row[:data] = row.slice!(*attrs)
Model.create(row)
end
PS Это, вероятно, может быть подано в "Stupid Ruby Tricks", но если вы используете Ruby 2.0+, вы можете воспользоваться преимуществом двойного сплата (**
) для этой компактной конструкции:
rows.each do |row|
Model.create(data: row.slice!(*attrs), **row)
end
PPS Если у вас большие CSV и у вас проблемы с производительностью (звоните create
несколько тысяч раз - и последующая база данных INSERT
s - это не дешево), я рекомендую проверить драгоценный камень activerecord-import. Он предназначен именно для такого рода вещей. С этим вы бы сделали что-то вроде этого:
rows = SmarterCSV.process(csv.path)
attrs = Model.attribute_names.map(&:to_sym)
models = rows.map do |row|
row[:data] = row.slice!(*attrs)
Model.new(row)
end
Model.import(models)
В документах activerecord-import есть и другие, более быстрые варианты.
Ты пытался:
row[:data] = row.delete_if {|k,v| !Model.attribute_method?(k) }
Model.create(row)
Это удалит элементы из хэша строки и добавит пары ключ-значение обратно в строку под :data
ключ.
Вы можете попробовать это has_attribute?
row[:data] = row.keep_if { |x| !Model.has_attribute?(x) }