简体   繁体   中英

How to get the right csv format from hash in ruby

Hash to csv

hash :

{
"employee" => [
  {
    "name" => "Claude",
    "lastname"=> "David",
    "profile" => [
       "age" => "43",
       "jobs" => [
         {
           "name" => "Ingeneer",
           "year" => "5"
         }
       ],
       "graduate" => [
          {
             "place" => "Oxford",
             "year" => "1990"
          },
       ],
       "kids" => [
         {
           "name" => "Viktor",
           "age" => "18",
         }
    ]
  }
}]

this is an example of an hash I would work on. So, as you can see, there is many level of array in it.

My question is, how do I put it properly in a CSV file?

I tried this :

column_names = hash['employee'].first.keys
s=CSV.generate do |csv|
   csv << column_names
   hash['scrap'].each do |x|
     csv << x.values
   end
end
File.write('myCSV.csv', s)

but I only get name , lastname and profile as keys, when I would catch all of them ( age , jobs , name , year , graduate , place ...).

Beside, how can I associate one value per case? Because I actually have all employee[x] which take a cell alone. Is there any parameters I have missed?

Ps: This could be the following of this post

If you want to convert the hash to a CSV file or record, you'll need to get a 'flat' representation of your keys and values. Something like the following:

h = {
 a: 1,
 b: {
  c: 3,
  d: 4,
  e: {
    f: 5
  },
  g: 6
 } 
}

def flat_keys(h)
  h.keys.reject{|k| h[k].is_a?(Hash)} + h.values.select{|v| v.is_a?(Hash)}.flat_map{|v| flat_keys(v)}
end

flat_keys(h)
# [:a, :c, :d, :g, :f]

def flat_values(h)
  h.values.flat_map{|v| v.is_a?(Hash) ? flat_values(v) : v}
end

flat_values(h)
# [1, 3, 4, 5, 6]

Then you can apply that to create a CSV output.

A valid CSV output has a fixed number of columns, your hash has a variable number of values. The keys jobs , graduate and kids could all have multiple values.

If your only goal is to make a CSV output that can be read in Excel for example, you could enumerate your Hash, take the maximum number of key/value pairs per key, total it and then write your CSV output, filling the blank values with "".

There are plenty of examples here on Stack Overflow, search for "deep hash" to start with.

Your result would have a different number of columns with each Hash you provide it.

That's too much work if you ask me. If you just want to present a readable result, your best and easiest option is to convert the Hash to YAML which is created for readability:

require 'yaml'
hash = {.....}
puts hash.to_yaml

employee:
- name: Claude
  lastname: David
  profile:
  - age: '43'
    jobs:
    - name: Ingeneer
      year: '5'
    graduate:
    - place: Oxford
      year: '1990'
    kids:
    - name: Viktor
      age: '18'

It depends on how those fields are represented in the database.

For example, your jobs has a hash with name key and your kids also has a hash with name key, so you can't just 'flatten' them, because keys have to be unique.

jobs is probably another model (database table), so you probably would have to (depending on the database) write it separately, including things like the id of the related object and so on.

Are you sure you're not in over your head? Judging from your last question and because you seem to treat csv's as simple key-values pair omitting all the database representation and relations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM