[Ruby] CSV ファイルを YAML ファイルへ変換する

フィールド数が分からなかったり、idが行頭にないファイルを、扱いやすい形に整える。

フィールド数が分からないファイルを変換する

いろいろ悩んでいて、こちらで解決させて頂きました。

こんなデータがあるとして

score.csv
name,score,id
yamada,32,1
suzuki,83,55
tokoro,63,14
csv2yaml.rb
require 'csv'
require 'yaml'
require 'pp'
path_to_csv = 'score.csv'
path_to_yaml = 'score.yaml'
csv = CSV.read(path_to_csv, :headers => true).map(&:to_hash)
hash = csv
File.open(path_to_yaml, 'w') { |f| f.write(hash.to_yaml) }
pp YAML.load_file(path_to_yaml)

こんな形式にまとまりました。

score.yaml
---
- name: yamada
  score: '32'
  id: '1'
- name: suzuki
  score: '83'
  id: '55'
- name: tokoro
  score: '63'
  id: '14'

CSV

:headers オプションで、CSV ファイルの一行目をヘッダとして扱ってくれる。

headers=>false
[["name", "score", "id"],
 ["yamada", "32", "1"],
 ["suzuki", "83", "55"],
 ["tokoro", "63", "14"]]
headers=>true
#<csv::Table mode:col_or_row row_count:4>
[[["name", "yamada"], ["score", "32"], ["id", "1"]],
 [["name", "suzuki"], ["score", "83"], ["id", "55"]],
 [["name", "tokoro"], ["score", "63"], ["id", "14"]]]

YAML

to_yaml メソッドで、hash を YAML ファイルとして保存する。
.load_file で、指定した YAML ファイルを読み込み、Ruby オブジェクトを返す。

YAML フォーマット

.map(&:to_hash)

mapメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。

map, map! (Array) – Rubyリファレンス

&:to_hash は、こういうこと。

.map { |item| item.to_hash }

フィールドに含まれている id を活用する

データには、どうやら id が含まれているようなので、これを利用できるようにしたい。

score.csv
name,score,id
yamada,32,1
suzuki,83,55
tokoro,63,14
csv2yaml.rb
require 'csv'
require 'yaml'
require 'pp'
path_to_csv = 'score.csv'
path_to_yaml = 'score.yaml'
csv = CSV.read(path_to_csv, :headers => true).map(&:to_hash)
hash = {}
csv.each do |row|
   id = row['id'].to_i
   hash[id] = row
end
File.open(path_to_yaml, 'w') { |f| f.write(hash.to_yaml) }
pp yaml = YAML.load_file(path_to_yaml)
pp yaml[55]['name']

こんな形式にまとまりました。

score.yaml
---
1:
  name: yamada
  score: '32'
  id: '1'
55:
  name: suzuki
  score: '83'
  id: '55'
14:
  name: tokoro
  score: '63'
  id: '14'

こうしておくと、yaml[55]['name']"suzuki" と直感的に取得できるので良さそう。

補遺

Rubyでヘッダ付きのCSVを生成する | deadwood