2

Need pointers on writing a hash to a csv with keys forming the column names and the key values being the column values.

Hash is of the format as below

hash = { 'A' => [ 'v', 'x', 'y' , 'z' ] , 'B' => [ 'm', 'n' , 'o' ] , 
'C' => [ 'i', 'j' , 'k' , 'l', 'm', 'n' , 'o' ] }

Desired CSV output

row 0 (headers) - 'A' , 'B' , 'C'
row 1           - 'v' , 'm' , 'i'
row 2           - 'x' , 'n' , 'j'
row 3           - 'y' , 'o' , 'k'
row 4           - 'z' , ''  , 'l'
row 5           - ''  , ''  , 'm'
row 6           - ''  , ''  , 'n'
row 7           - ''  , ''  , 'o'

Tried the following -

csv = CSV.open ("file.csv" , 'wb', headers: true)
hash.each do |k, v|
csv[k] = v         # CSV::Table has a []= method
end

Was thinking csv[k] = v should work, but it doesn't.

0

2 Answers 2

1
CSV.open("/tmp/file.csv", 'wb') do |csv|
   csv << hash.keys
   max_len = hash.values.map(&:length).max
   (0...max_len).zip(*hash.values).each do |_, *row|
    csv << row
  end
end

EDIT I initially used row.compact to match your output, but it doesn't seem to be consistent. Row 4 should look like "z,", not "z"

Sign up to request clarification or add additional context in comments.

2 Comments

I just edited the question. The approach works to some extend, but falls a tad short of matching the output. This seems iterate only up the number of values for the first key in the hash. So row 5, 6 and 7 above never gets written into the csv.
Right, I overlooked this scenario. Edited my answer.
1

I would do the same as below :

require 'csv'

output_file_path = File.expand_path('output.csv',File.dirname(__FILE__))

hash = {   
           'A' => [ 'v', 'x', 'y' , 'z' ] ,
           'B' => [ 'm', 'n' , 'o' ] ,
           'C' => [ 'i', 'j' , 'k' , 'l', 'm', 'n' , 'o' ]
       }

option = { 
           :headers => hash.keys,
           :write_headers => true,
           :force_quotes => true,
           :converters => CSV::Converters[:nil_to_string] = lambda do |field| 
             field.to_s
           end
         }
max_value_size = hash.values.map(&:size).max

CSV.open(output_file_path,'w',option) do |csv|
  key_ary = hash.keys
  max_value_size.times do |index|
    csv << key_ary.map { |k| hash[k][index] } 
  end
end

output :

"A","B","C"
"v","m","i"
"x","n","j"
"y","o","k"
"z","","l"
"","","m"
"","","n"
"","","o"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.