下記のようにデータレコードとそのヘッダが別々のファイルに保存されているような場合に、2ファイルから1つのCSV::Tableをつくりたい。
header.csv
id, name, score
data.csv
1, john, 10
2, paul, 12
結論としては1.のheadersにヘッダーの配列を渡す方法がすっきり確実そうです。
1. オプションのheadersにヘッダーの配列を渡す
@hanachin_ さんから教えていただいた方法。すっきりしていい感じ…!
require 'csv'
headers = CSV.parse_line(File.read("header.csv"))
p CSV.table('data.csv', headers: headers)
CSV.table('data.csv', headers: headers).each do |r|
p r.to_h
end
# #<CSV::Table mode:col_or_row row_count:3>
# {:id=>1, :name=>" john", :score=>10}
# {:id=>2, :name=>" paul", :score=>12}
2. ヒアドキュメントでがっちゃんこする
@yancya さんに教えていただいた方法。そんな技が…。知らなかったです。
require "csv"
header = File.read("header.txt").chomp
rows = File.read("data.csv").chomp
table = CSV.parse(<<CSV, headers: true)
#{header}
#{rows}
CSV
p table
# => #<CSV::Table mode:col_or_row row_count:3>
3. シェルコマンドとCSV.newの合せ技
さすがにTempfileを使う方法から頭悪すぎる感が滲んでいたので、周りのエンジニアの方にもお知恵をいただいた結果かなりスッキリしてきました。
str = %x(cat header.csv; echo ""; cat data.csv)
p CSV.new(str, headers: true, header_converters: :symbol).read
# => #<CSV::Table mode:col_or_row row_count:3>
ただしこの方法だと、末尾の改行だったり、文字列が"
で囲われているかどうかだったりと、色々なことが整った状態でないとうまくいきません。
4. Tempfileを使う
最初に試した方法。
CSV.table('path/to/csv')
本来であれば上記のように1ファイルでサクッとできるのだが、2ファイルに分かれてしまっているので、Tempfile
を使ってヘッダをくっつけた仮のCSVファイルを生成し、そこからCSV::Tableをつくってやる。
header = File.read('header.csv')
data = File.read('data.csv')
csv = nil
Tempfile.open do |t|
t.write header
t.write "\n"
t.write data
csv = CSV.table(t.path)
end
p csv
# => #<CSV::Table mode:col_or_row row_count:3>