search
LoginSignup
4

More than 5 years have passed since last update.

posted at

updated at

RubyでCSVのヘッダーとデータが別々の2ファイルからCSV::Tableをつくる

下記のようにデータレコードとそのヘッダが別々のファイルに保存されているような場合に、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>

Ref

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4