What is the second parameter/argument to CSV.open( ) in ruby?
From the IO Open Mode documentation:
"r" Read-only, starts at beginning of file (default mode).
"r+" Read-write, starts at beginning of file.
"w" Write-only, truncates existing file
to zero length or creates a new file for writing.
"w+" Read-write, truncates existing file to zero length
or creates a new file for reading and writing.
"a" Write-only, starts at end of file if file exists,
otherwise creates a new file for writing.
"a+" Read-write, starts at end of file if file exists,
otherwise creates a new file for reading and
writing.
CSV code in Ruby
Reading the comments, I think you should first make yourself familiar with the basic building blocks and the syntax of Ruby.
I know, in the beginning I had tons of similar questions.
I found a very basic and playful approach to learn the basics of the Ruby programming language in this book.
To answer your question:
CSV.foreach(csv_file_name, header: true)
is a method call to CSV
. The method foreach
in this case receives two arguments.
csv_file_name
is a variable that is being defined as the first argument of the outer methodread_in_csv_data
.So if you call
CsvReader.new.read_in_csv_data("file.csv")
it will pass the string"file.csv"
down toCSV.foreach("file.csv", ...
.header: true
is basically a Hash, but this style is commonly known as named arguments. Very useful and readable if you want to pass down options to a method that are very specific.How does
CSV.foreach
receiveheader: true
?
That is easy to find out, open up a$ irb
session on your local machine, and define the following method:def foo(arg1, arg2)
p arg1
p arg2
nil
endNow play around by calling
foo
with different arguments.> foo("string", 123)
"string"
123
=> nil
> foo("string", named: "argument")
"string"
{:named=>"argument}
=> nil
> foo("string", { named: "argument" })
"string"
{:named=>"argument}
=> nilDoing this you might grasp how Ruby handles named arguments or hash arguments. As you see, the latter examples above show that you can specify a Hash without
{}
as the last argument on any method in Ruby.header: true
represents a Hash, ie{:header => true}
.What does
header: true
trigger?
This has to be read up in the documentation forCSV::foreach
. You you do a Google search for "ruby csv foreach" you will quickly find a documentation. Here is a direct link toCSV::foreach
.
There you can read "The options parameter can be anything ::new understands.". So you gotta look for the documentation ofCSV::new
. This leads you to a nice description of each option you can pass toCSV::new
orCSV::foreach
.What does the documentation say?
:headers If set to :first_row or true, the initial row of the CSV file
will be treated as a row of headers. If set to an Array, the contents
will be used as the headers. If set to a String, the String is run
through a call of ::parse_line with the same :col_sep, :row_sep, and
:quote_char as this instance to produce an Array of headers. This
setting causes #shift to return rows as CSV::Row objects instead of
Arrays and #read to return CSV::Table objects instead of an Array of
Arrays.
Your other questions:
You are welcome to ask questions, but I also think it would be very good for you to get someone to help you directly. Maybe find yourself a local Ruby user group. People there are usually very friendly and helpful.
Having said that, I will now explain what row["ISBN"]
means:
CSV::foreach
is a method that yields a block. This block is somewhat similar to a function, and per definition of CSV::foreach
it lets you do something with each row of the csv file.
row
itself is just a variable name defined in the block arguments (ie |row|
).
Maybe its time for you to open up another $ irb
and play around with blocks yourself:
def foo
yield 123
end
Try calling foo the same way as you do CSV::foreach
, just that you print the output.
> foo do |row| puts row end
123
=> nil
> foo { |arg1| puts arg1 }
123
=> nil
As you see yield 123
is passing 123
as the first argument of the block. You take the first argument in a variable (eg row
or arg1
) and you can do something with it in the block. Also note that you can create these blocks with do |arg1| ... end
but also { |arg1| ... }
.
So what is it with this ["ISBN"]
??
row
itself is just a variable, but is also an object so you can call methods on it. In Ruby you might have already encountered a hash.
The contents of a hash in Ruby can be accessed by calling []
on the hash.
The same way each row of the CSV is represented. Just that each row (since you told CSV
to use :headers) is being represented as a hash-like object.
If you omit :headers each row will be an array of arrays.
Maybe its time for you to open up an $ irb
and play around with CSV
directly. For that create yourself a simple csv file like the following, and save it to disk:
id,name
1,Overbryd
2,Boniface
3,Poignant
Then, open up a $ irb -r csv
.
> CSV.foreach("test.csv") { |r| p r }
["id", "name"]
["1", "Overbryd"]
["2", "Boniface"]
["3", "Poignant"]
=> nil
> CSV.foreach("test.csv", headers: true) { |r| p r }
#<CSV::Row "id":"1" "name":"Overbryd">
#<CSV::Row "id":"2" "name":"Boniface">
#<CSV::Row "id":"3" "name":"Poignant">
=> nil
> CSV.foreach("test.csv", headers: true) do |row|
puts row["id"]
end
1
2
3
=> nil
Also, please accept one of the given answers. People take time to answer your questions, their only reward is if you accept one of their answers.
Ruby CSV Open - Wrong number of arguments in Ruby 3.0
In Ruby 3.0, positional arguments and keyword arguments was separated, with deprecation in Ruby 2.7. That means, that the Ruby 2.7 deprecation warning: Using the last argument as keyword parameters is deprecated,
ie. using a hash as argument for **options
is no longer supported.
You'll have to call it either as:
# Manual spreading hash
CSV.open(csv_path, "wb", **{:col_sep => ";"}) do |csv|
...
end
# Or as Keyword argument
CSV.open(csv_path, "wb", :col_sep => ";") do |csv|
...
end
EDIT: See also https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
What are the Ruby File.open modes and options?
In Ruby IO module documentation, I suppose.
Mode | Meaning
-----+--------------------------------------------------------
"r" | Read-only, starts at beginning of file (default mode).
-----+--------------------------------------------------------
"r+" | Read-write, starts at beginning of file.
-----+--------------------------------------------------------
"w" | Write-only, truncates existing file
| to zero length or creates a new file for writing.
-----+--------------------------------------------------------
"w+" | Read-write, truncates existing file to zero length
| or creates a new file for reading and writing.
-----+--------------------------------------------------------
"a" | Write-only, starts at end of file if file exists,
| otherwise creates a new file for writing.
-----+--------------------------------------------------------
"a+" | Read-write, starts at end of file if file exists,
| otherwise creates a new file for reading and
| writing.
-----+--------------------------------------------------------
"b" | Binary file mode (may appear with
| any of the key letters listed above).
| Suppresses EOL <-> CRLF conversion on Windows. And
| sets external encoding to ASCII-8BIT unless explicitly
| specified.
-----+--------------------------------------------------------
"t" | Text file mode (may appear with
| any of the key letters listed above except "b").
What does rb:bom|utf-8 mean in CSV.open in Ruby?
When reading a text file in Ruby you need to specify the encoding or it will revert to the default, which might be wrong.
If you're reading CSV files that are BOM encoded then you need to do it that way.
Pure UTF-8 encoding can't deal with the BOM header so you need to read it and skip past that part before treating the data as UTF-8. That notation is how Ruby expresses that requirement.
Ruby CSV - Create a file and edit a file in one .rb and then open and edit it starting where the last .rb left off in another .rb?
The second argument of the open
method is the write mode.
See here: https://stackoverflow.com/a/17866433/9595653
And also refer to the documentation for CSV
here:
https://ruby-doc.org/stdlib-2.6.1/libdoc/csv/rdoc/CSV.html
You will want to change the option to "a"
Saying there are 0 arguments when I have 2? Trying to open a CSV file to write into
I believe the problem is with Dir.foreach
, not CSV.open
.
You need to supply a directory to foreach as an argument. That's why you are getting the missing argument error.
Try:
Dir.foreach('/path/to_my/directory') do |current_file|
I think the open
that is referenced in the error message is when Dir
is trying to find the directory to open in order to get the file list for foreach
.
Writing data into a CSV file by two different CSV files
Cleaning up ecode.csv
made it more challenging, but here is what I came up with:
In case, data.csv
and ecode.csv
are matched by row numbers:
require 'csv'
data = CSV.read('data.csv', headers: true).to_a
headers = data.shift << 'eppocode'
double_quoted_ecode = CSV.read('ecode.csv')
ecodeIO = StringIO.new
ecodeIO.puts double_quoted_ecode.to_a
ecodeIO.rewind
ecode = CSV.parse(ecodeIO, headers: true)
CSV.open('plantas.csv', 'w+') do |plantas|
plantas << headers
data.each.with_index do |row, idx|
planta = row + [ecode['code'][idx]]
plantas << planta
end
end
Using your example files, this gives you the following plantas.csv
:
id,type,code,name_es,name_ca,name_en,latin_name,customer_id,eppocode
7205,DunSpecies,NULL,0,0,0,"",11630,LEECO
7437,DunSpecies,NULL,0,Xicoira,0,"",5273,LEECO
In case, entries are matched by data.csv
's id
and ecode.csv
's identifier
:
require 'csv'
data = CSV.read('data.csv', headers: true)
headers = data.headers << 'eppocode'
double_quoted_ecode = CSV.read('ecode.csv')
ecodeIO = StringIO.new
ecodeIO.puts double_quoted_ecode.to_a
ecodeIO.rewind
ecode = CSV.parse(ecodeIO, headers: true)
CSV.open('plantas.csv', 'w+') do |plantas|
plantas << headers
data.each do |row|
id = row['id']
ecode_row = ecode.find { |entry| entry['identifier'] == id } || {}
planta = row << ecode_row['code']
plantas << planta
end
end
I hope you find this helpful.
Related Topics
Define a Method That Is a Closure in Ruby
Switching Between Web and Touch Interfaces on Facebook Login Using Omniauth and Rails 3
Testing Whether String Starts with or End with Another String
Should I Use Class Method or Instance Method, and Why
Rails - Testing JSON API with Functional Tests
What's the Difference Between a Class and the Singleton of That Class in Ruby
Creating an Md5 Hash of a Number, String, Array, or Hash in Ruby
No Database Connection in Rails Console
Rails 4.2 - Sidekiq Not Sending Emails in Development
Bundle Install: Error: Failed to Build Gem Native Extension. Nio4R Gem
How to Install Rvm Without Root Access