- ベストアンサー
1つのキーに複数の値があるハッシュ(連想配列)をcsvに出力する
表形式のデータにおいて、列数が行によって可変長となる場合のエレガントな方法を探しています。 ■やりたいこと csv形式の元データを1行可変列に変換し、excelに読み込ませる。 excelに読み込ませるデータをrubyで処理したい。 処理前のデータ コード、属性ともにユニークではない コードは数値型、属性は文字型 コード 人数1 人数2 属性 1 1 100 A 2 1 0 B 3 4 1 A 2 5 8 C 4 1 122 D 処理後の最終形態 コードは重複なし。 コード 属性 1 A 2 B,C 3 A 4 D この処理を 元のデータを1行ずつ読み出す ハッシュに追加する ハッシュをcsvに書き出す と考えて、下記のようなコードを書きましたが、属性部分がカンマで区切られず、配列の値がすべて連結されてしまいます。 ■質問 こういうデータを処理するときにどうすればいいか? (後学のために、なぜそうしたほうがよいのかを教えていただけると幸いです) 具体的にこのコードを書き直すとしたら、どこをどう変更すればよいか? いろいろなリファレンスを読んだのですが、列数が可変長となる場合のサンプルが見つけられなくて詰まってしまいました。 よろしくお願いいたします。 環境:active ruby 1.8.6 , windows XP SP3 , ■作成したコード(抜粋) require 'csv' in_path='C:\Documents and Settings\a\デスクトップ\解凍後データ\dummy.txt' out_path='C:\Documents and Settings\a\デスクトップ\解凍後データ\out.txt' #set initial values col_code=0 'コード列位置 col_zokusei=3 '属性列位置 h=Hash.new{|h,key| h[key]=[]} CSV.foreach(in_path){|row| h[row[col_code]]<<row[col_zokusei] } #put hash to csv file. CSV.generate(out_path){ |writer| h.to_a.each{ |row| p row writer << row } }
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
何故そうなるかは、["2",["B","C"]] というデータをCSV出力しようとするので、1項目が"2"でこれは文字列でそのままOK。2項目は["B","C"]で文字列でなく配列なので、["B","C"].to_s で文字列化されます。この結果は"BC"になります。 2,B,Cと出力したければ、#1の方の回答の通り、["2",["B","C"]].flatternで、["2","B","C"]に変換して出力するといいかと。 2,"B,C"にしたいとか、もっと全体をすっきりさせたいとすると、属性保存の所で、配列を借用するのじゃなくて自分でクラスを定義して、to_sも定義します。 require 'csv' in_path=・・・・・ out_path=・・・・・ class Attr def initialize; @data = []; end def <<(other); @data |= [other]; end # 集合の和集合(重複を省く) def to_s; @data.join(","); end end h=Hash.new{|h,key| h[key]=Attr.new} CSV.foreach(in_path){|key,n1,n2,attr| h[key.data.to_i] << attr.data } CSV.generate(out_path){ |writer| h.sort.each{ |row| #ソートした方が良いかと writer << row } }
その他の回答 (1)
- ryu_chan
- ベストアンサー率37% (69/186)
部分的な回答となりますが、最後の writer << row を writer << row.flatten にすると所望の結果は得られないでしょうか?
お礼
確かに、所望の結果になりました! ご回答ありがとうございました。
お礼
解説ありがとうございました。 flattenメソッドの意味が分かりました。 rubyリファレンスマニュアルを読むと、確かに書いてありますね。 「ネストした配列を平滑化してそれを返します。」ってそういう意味か~と納得しました。