views:

267

answers:

1

I have a hash of hashes (@post) and I want to save it into csv. Strange for me is that my approach works for the header but not for the rows. If I display the rows using

csv << (@post_csv_order.each {|element| puts single_post[element]})

I can see the right strings on the screen but the csv files contains values of the hash key not the values of the hash values. Basically I have 5 lines of the header in my csv instead of the right values.

I think I need to convert @post[key] hash into an array with an order based on @post_csv_order (contains keys for the @post[key] hash) There is a hash method to_a but it will not give me the order I need.

I guess you might suggest different approach and not to use hash of hashed but I would like to also know how to convert hash into an array that has some pre-defined order.

On the other hand if there is any easy way how to save a hash of hashes into csv/table I am happy to rewrite my code.

  require 'rubygems'
  require 'faster_csv'
  require 'pp'

  @post = {123 => {"ForumID" => "123", "Post author" => "Thom" }, 
           222 => {"ForumID" => "222", "Post author" => "Mark"},
           345 => {"ForumID" => "345", "Post author" => "Eddy"},
           444 => {"ForumID" => "444", "Post author" => "Eli"}}
  @post_csv_order = [
                      "ForumID" ,
                      "Post author"  
                     ]

  FasterCSV.open("test.csv", "w", { :quote_char => '"', :col_sep =>',', :row_sep =>:auto, :headers => true, :return_headers => false, :force_quotes => true}) do |csv|
    csv << (@post_csv_order.each {|element| element})
       @post.each_value do |single_post|
        csv << (@post_csv_order.each {|element| single_post[element]})  
     #csv << (@post_csv_order.each {|element| puts single_post[element]})
      end 
  end
+2  A: 

This line:

csv << @post_csv_order.each {|element| single_post[element]}

loops over the @post_csv_order array and returns the array itself, which is not what you want. The each and the block don't actually do anything in this context. If you change each to map it will do what you want:

csv << @post_csv_order.map {|element| single_post[element]}

map returns a new array containing the result of each invocation of the block.

Theo
@Theo: thank you for the solution. I do not understand why `csv << (@post_csv_order.each {|element| element})` for headers work
Radek
It works because all it does is `csv << @post_csv_order`, and `@post_csv_order` contains the headers. The `.each { |element| element }` (besides being a no-op in itself) has no effect. `each` returns the array itself, the receiver of the message. `each` is just a looping construct and it's only useful if the loop has side-effects, which your loops don't. You're using `each` as if it were `map`.
Theo