How do I skip headers while writing CSV? - ruby

I am writing a CSV file and CSV.dump outputs two header lines which I don't want.
I tried setting :write_headers => false but still it outputs a header:
irb> A = Struct.new(:a, :b)
=> A
irb> a = A.new(1,2)
=> #<struct A a=1, b=2>
irb> require 'csv'
=> true
irb> puts CSV.dump [a], '', :write_headers => false, :headers=>false
class,A
a=,b=
1,2

I don't think you can do it with option parameters. But you can easily accomplish what you want by not using the generate method
irb> arr = [a, a]
=> [#<struct A a=1, b=2>, #<struct A a=1, b=2>]
irb> csv_string = CSV.generate do |csv|
irb* arr.each {|a| csv << a}
irb> end
irb> puts csv_string
1,2
1,2
=> nil

I think the problem is two-fold:
CSV.dump [a]
wraps an instance of the struct a in an array, which then CSV tries to marshall. While that might be useful sometimes, when trying to generate a CSV file for consumption by some other non-Ruby app that recognizes CSV, you're going to end up with values that can't be used. Looking at the output, it isn't CSV:
class,A
a=,b=
1,2
Looking at it in IRB shows:
=> "class,A\na=,b=\n1,2\n"
which, again, isn't going to be accepted by something like a spreadsheet or database. So, another tactic is needed.
Removing the array from a doesn't help:
CSV.dump a
=> "class,Fixnum\n\n\n\n"
Heading off a different way, I looked at a standard way of generating CSV from an array:
puts a.to_a.to_csv
=> 1,2
An alternate way to create it is:
CSV.generate do |csv|
csv << a.to_a
end
=> "1,2\n"

Related

How to use a variable for a file path? Ruby

Is there the possibility in Ruby to use a variable / string to define a file path?
For example I would like to use the variable location as follow:
location = 'C:\Users\Private\Documents'
#### some code here ####
class Array
def to_csv(csv_filename)
require 'csv'
CSV.open(csv_filename, "wb") do |csv|
csv << first.keys # adds the attributes name on the first line
self.each do |hash|
csv << hash.values
end
end
end
end
array = [{:color => "orange", :quantity => 3},
{:color => "green", :quantity => 1}]
array.to_csv('location\FileName.csv')
You can use variable inside string, following way:
array.to_csv("#{location}\FileName.csv")
You can use File.join, which accepts variables as arguments.
irb(main):001:0> filename = File.basename('/home/gumby/work/ruby.rb')
=> "ruby.rb"
irb(main):002:0> path = '/home/gumby/work'
=> "/home/gumby/work"
irb(main):003:0> File.join(path, filename)
=> "/home/gumby/work/ruby.rb"
As noted above, if you start embedding slashes in your strings things may get unmanageable in future.

Ruby: Reading Arrays of Hashes from YAML

I have two dads going into my YAML file, but only one family comes out. What happened to Sam? How do I get both out?
## dads.rb
require 'yaml'
require 'pp'
dad=[]
dad[0] = {:name => "Joe", :kids => ["Mary", "John"]}
dad[1] = {:name => "Sam", :kids => ["Sam Jr", "Samantha", "Samizdat"]}
open('dads.yml' , 'w') do |f|
dad.each do |d|
f.write YAML::dump(d)
end
end
family = []
open('dads.yml') do |f|
family << YAML::load(f.read)
end
pp fams
You dump multiple YAML documents but only read back one. Instead, you can just dump and read the whole array:
require 'yaml'
dads = []
dads << {:name => "Joe", :kids => ["Mary", "John"]}
dads << {:name => "Sam", :kids => ["Sam Jr", "Samantha", "Samizdat"]}
open('dads.yml', 'w') { |f| YAML::dump(dads, f) }
family = YAML::load(File.read('dads.yml'))
p family
Your code currently creates separate "documents" within the YAML output. By default, YAML::load will just read in the first document. Niklas' answer is definitely the way you should go, but if you absolutely had to deal with multiple documents, you could use the load_documents method:
family = YAML.load_documents(File.read("dads.yml"))
# => [{:name=>"Joe", :kids=>["Mary", "John"]}, {:name=>"Sam", :kids=>["Sam Jr", "Samantha", "Samizdat"]}]

hash assignment when (key => value) are stored in an array? (ruby)

I have hash (#post) of hashes where I want to keep the order of the hash's keys in the array (#post_csv_order) and also want to keep the relationship key => value in the array.
I don't know the final number of both #post hashes and key => value elements in the array.
I don't know how to assign the hash in a loop for all elements in the array. One by one #post_csv_order[0][0] => #post_csv_order[0][1] works nicely.
# require 'rubygems'
require 'pp'
#post = {}
forum_id = 123 #only sample values.... to make this sample script work
post_title = "Test post"
#post_csv_order = [
["ForumID" , forum_id],
["Post title", post_title]
]
if #post[forum_id] == nil
#post[forum_id] = {
#post_csv_order[0][0] => #post_csv_order[0][1],
#post_csv_order[1][0] => #post_csv_order[1][1]
##post_csv_order.map {|element| element[0] => element[1]}
##post_csv_order.each_index {|index| #post_csv_order[index][0] => #post_csv_order[index][1] }
}
end
pp #post
desired hash assignment should be like that
{123=>{"Post title"=>"Test post", "ForumID"=>123}}
The best way is to use to_h:
[ [:foo,1],[:bar,2],[:baz,3] ].to_h #=> {:foo => 1, :bar => 2, :baz => 3}
Note: This was introduced in Ruby 2.1.0. For older Ruby, you can use my backports gem and require 'backports/2.1.0/array/to_h', or else use Hash[]:
array = [[:foo,1],[:bar,2],[:baz,3]]
# then
Hash[ array ] #= > {:foo => 1, :bar => 2, :baz => 3}
This is available in Ruby 1.8.7 and later. If you are still using Ruby 1.8.6 you could require "backports/1.8.7/hash/constructor", but you might as well use the to_h backport.
I am not sure I fully understand your question but I guess you want to convert a 2d array in a hash.
So suppose you have an array such as:
array = [[:foo,1],[:bar,2],[:baz,3]]
You can build an hash with:
hash = array.inject({}) {|h,e| h[e[0]] = e[1]; h}
# => {:foo=>1, :bar=>2, :baz=>3}
And you can retrieve the keys in correct order with:
keys = array.inject([]) {|a,e| a << e[0] }
=> [:foo, :bar, :baz]
Is it what you were looking for ?
Answers summary
working code #1
#post[forum_id] = #post_csv_order.inject({}) {|h,e| h[e[0]] = e[1]; h}
working code #2
#post[forum_id] = Hash[*#post_csv_order.flatten]
working code #3
#post[forum_id] ||= Hash[ #post_csv_order ] #requires 'require "backports"'

Ruby equivalent of Perl Data::Dumper

I am learning Ruby & Perl has this very convenient module called Data::Dumper, which allows you to recursively analyze a data structure (like hash) & allow you to print it. This is very useful while debugging. Is there some thing similar for Ruby?
Look into pp
example:
require 'pp'
x = { :a => [1,2,3, {:foo => bar}]}
pp x
there is also the inspect method which also works quite nicely
x = { :a => [1,2,3, {:foo => bar}]}
puts x.inspect
I normally use a YAML dump if I need to quickly check something.
In irb the syntax is simply y obj_to_inspect. In a normal Ruby app, you may need to add a require 'YAML' to the file, not sure.
Here is an example in irb:
>> my_hash = {:array => [0,2,5,6], :sub_hash => {:a => 1, :b => 2}, :visible => true}
=> {:sub_hash=>{:b=>2, :a=>1}, :visible=>true, :array=>[0, 2, 5, 6]}
>> y my_hash # <----- THE IMPORTANT LINE
---
:sub_hash:
:b: 2
:a: 1
:visible: true
:array:
- 0
- 2
- 5
- 6
=> nil
>>
The final => nil just means the method didn't return anything. It has nothing to do with your data structure.
you can use Marshal, amarshal, YAML

Ruby: How to make IRB print structure for Arrays and Hashes

When I make a new array/hash in irb, it prints out a nice format to show the structure, ex.
["value1", "value2", "value3"]
{"key1" => "value1"}
... but when I try to print out my variables using puts, I get them collapsed:
value1
value2
value3
key1
value1
I gather that puts is not the right command for what I want, but what is? I want to be able to view my variables in irb in the first format, not the second.
You can either use the inspect method:
a=["value1", "value2", "value3"]
puts a.inspect
Or, even better, use the pp (pretty print) lib:
require 'pp'
a=["value1", "value2", "value3"]
pp a
Another thing you can do is use the y method which converts input into Yaml. That produces pretty nice output...
>> data = { 'dog' => 'Flemeale', 'horse' => 'Gregoire', 'cow' => 'Fleante' }
=> {"cow"=>"Fleante", "horse"=>"Gregoire", "dog"=>"Flemeale"}
>> y data
---
cow: Fleante
horse: Gregoire
dog: Flemeale
The pretty print works well, but the Awesome_Print gem is even better! You will have to require awesome_print but it handles nested hashes and arrays beautifully plus colors them in the Terminal using 'ap' instead of 'p' to puts the output.
You can also include it in your ~/.irbrc to have this as the default method for displaying objects:
require "awesome_print"
AwesomePrint.irb!
Try .inspect
>> a = ["value1", "value2", "value3"]
=> ["value1", "value2", "value3"]
>> a.inspect
=> "[\"value1\", \"value2\", \"value3\"]"
>> a = {"key1" => "value1"}
=> {"key1"=>"value1"}
>> a.inspect
=> "{\"key1\"=>\"value1\"}"
You can also use the p() method to print them:
>> p a
{"key1"=>"value1"}
My personal tool of choice for this is 'Pretty Print' and the pp method
require 'pp' # <- 'Pretty Print' Included in ruby standard library
pp({ :hello => :world, :this => ['is', 'an', 'array'] })
=> {:hello=>:world, :this=>["is", "an", "array"]}

Resources