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) }
Другие вопросы по тегам