Ruby CSVで重複したヘッダーを置換

10774 ワード

はじめに

1行目をヘッダーとするCSVファイルで、重複しているものをRubyで取り込む機会がありました。
その際、重複しているヘッダーの項目も取得できるように重複しているものを置換する方法を調査しました。
(最終的にはCSVファイルのヘッダーを重複しないように出力してもらうようになったので、実際に使用することはなかったです)

環境

  • Ruby 3.1.1

コード

require 'csv'

raw_data = "a,b,b,c,d,,e\naa,bb,bbbb,cc,dd,,ee"
csv_table = CSV.new(raw_data, headers: :first_row).read

headers = csv_table.headers
duplicated_headers = headers.select { |header| headers.count(header) > 1 }.uniq

convert_header_map = {}
convert_header_seq = {}
headers.each_with_index do |header, index|
  if duplicated_headers.include?(header)
    seq = convert_header_seq[header]
    seq = seq.nil? ? 1 : seq + 1
    convert_header_map[index] = "#{header}_#{seq}"
    convert_header_seq[header] = seq
  end
end

header_converters = [lambda { |field, field_info| convert_header_map[field_info.index] || field }]
csv_table2 = CSV.new(raw_data, headers: :first_row, header_converters: header_converters).read
irb(main):068:0> csv_table2[0]
=> #<CSV::Row "a":"aa" "b_1":"bb" "b_2":"bbbb" "c":"cc" "d":"dd" nil:nil "e":"ee">
irb(main):069:0> csv_table2[0]['b_1']
=> "bb"
irb(main):070:0> csv_table2[0]['b_2']
=> "bbbb"

内容

1回CSVを読み込み、重複しているヘッダーを抽出

csv_table = CSV.new(raw_data, headers: :first_row).read

headers = csv_table.headers
duplicated_headers = headers.select { |header| headers.count(header) > 1 }.uniq

重複しているヘッダーに対して、変換用のハッシュを生成

convert_header_map = {}
convert_header_seq = {}
headers.each_with_index do |header, index|
  if duplicated_headers.include?(header)
    seq = convert_header_seq[header]
    seq = seq.nil? ? 1 : seq + 1
    convert_header_map[index] = "#{header}_#{seq}"
    convert_header_seq[header] = seq
  end
end

変換用ハッシュを使用し、ヘッダーを変換を指定してCSVを再度読み込み

ヘッダーの変換はheader_convertersで指定