Issue writing data to csv file in ruby - ruby

I have an issue writing to a CSV file in ruby and can't figure out.
> header = "Full Name, Email, Phone Number" csv_file =
> CSV.open("myfile.csv", "wb") do |csv|
> csv << header
> inactive_users_id.each do |inactive_id| #inactive_users_id is just an array on integers, which represent user id
> full_name = User.find(inactive_id).full_name
> email = User.find(inactive_id).email
> phone_num = User.find(inactive_id).phone_number
> desired_info = Array.new
> desired_info.push(full_name)
> desired_info.push(email)
> desired_info.push(phone_num)
> csv << desired_info
> end
> end
I do get following errors:
undefined method `map' for "Full Name, Email, Phone
Number":String
if I remove line csv << header my csv file is
literally like this: [138] --> which represents the id and other
things are not there.
What could be the issue?
Thank you!

While I cannot replicate your issues please try this instead:
headers = ["Full Name", "Email", "Phone Number"]
CSV.open("myfile.csv", "wb", write_headers: true, headers: headers ) do |csv|
User.where(id: inactive_users_id)
.pluck(:full_name, :email, :phone_number)
.each do |row|
csv << row
end
end
Here we are collecting all the Users in 1 query (rather than your current 3 per User) and just the needed columns are converted to an Array (using #pluck). Then we just push each one into the csv as a row
As pointed out in the comments in rails 3 pluck will not work with multiple columns (or at all depending on version) instead one should use select and then reference the attributes inside the loop to to create the row

Related

How to generate a column in a CSV file instead of a row

So what I'm trying to do is read a specific column from an existing CSV file, parse some information out of the pulled data, then make a new CSV file with the newly parsed information in a single column. The header and first entry of the generated array go into the CSV file correctly, but after those, every other entry goes into the adjacent cells of the same row instead of creating a column, so it's like an L shape instead of just a line. Any ideas?
#!ruby.exe
require 'csv'
puts "Please enter a file name:" #user input file name (must be in same
folder as this file)
file = gets.chomp
begin
File.open(file, 'r')
rescue
print "Failed to open #{file}\n"
exit
end #makes sure that the file exists, if it does not it posts an error
data_file = File.new(file)
data = [] #initializes array for addresses from .csv
counter=0 #set counter up to allow for different sized files to be used
without issue
CSV.foreach(data_file, headers: true) do |row|
data << row.to_hash
counter+=1
end #goes through .csv one line ar a time
data.reject(&:empty?)
puts "Which column do you want to parse?"
column = gets.chomp
i=0
streets = []
while (i<counter)
address = data[i][column]
street_name = address.gsub(/^((\d[a-zA-Z])|[^a-zA-Z])*/, '')
streets.push(street_name)
i+=1
end
streets.reject(&:empty?)
puts "What do you want the output to be called?"
new_file = gets.chomp
CSV.open(new_file, "w", :write_headers=> true, :headers => [column]) do |hdr|
hdr << streets
end
You should scan the street array and insert it as a row, which means you need to place the line of data into an array before to send to the csv. Ok, maybe the code is simpler than the explanation:
CSV.open(new_file, "w", :write_headers=> true, :headers => [column]) do |csv_line|
streets.each { |street| csv_line << [street] }
end

Access csv data from htttp request in ruby

I'm trying to access the csv data, which I recive if I make a http-request.
I don't save it to a csv file, so I save it to the variable.
Let's say this is the response I get, how can I print food?
uuid,event_id,category
12,1,food
13,2,cars
And this is the part of the ruby code which is important.
That's something I found, but it was originally used with a file, so it doesn't work.
csvdata = request(action,parameter)
#data_hash = {}
CSV.foreach(csvdata) do |row|
uuid, event_id, category = row
#data_hash[uuid] = event_id
end
Do I really need files for that or is there a easy way I can access the values?
Update
CSV.parse(csvdata,data = Hash.new) do |row|
puts data
end
The hash should look like this so I can use the column names
{"uuid" => "12,13", "event_id" => "323,3243", "category" => "food,cars"}
csv_data = Hash.new{|k, v| k[v] = []}
CSV.parse(csv_string, headers: true) do |row|
row.each{|k, v| csv_data[k] << v}
end
csv_data = Hash[csv_data.map{|k, v| [k, v.join(",")]}]
Update after specification Requested output.
Try this:
csvdata = request(action,parameter)
#data_hash = {}
CSV.parse(csvdata, headers: true) do |row|
#data_hash[row['uuid']] = row['event_id']
end
#data_hash
# => {"12"=>"1", "13"=>"2"}
When you parse a CSV, the seconds parameter (data = Hash.new in your code) is actually an options parameter. You can see the available options here:
: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.
When passing headers: true - values are parsed into a Row object, where they can be accessed by name.

Ruby: Write a value to a specific location in CSV file

I'm still fairly new to coding and I'm trying to learn about manipulating CSV files.
The code below opens a specified CSV file, goes to each url in the CSV file in column B (header = url), and finds the price on the webpage.
Example data from CSV file:
Store,URL,Price
Walmart,http://www.walmart.com/ip/HP-11.6-Stream-Laptop-PC-with-Intel-Celeron-Processor-2GB-Memory-32GB-Hard-Drive-Windows-8.1-and-Microsoft-Office-365-Personal-1-yr-subscription/39073484
Walmart,http://www.walmart.com/ip/Nextbook-10.1-Intel-Quad-Core-2-In-1-Detachable-Windows-8.1-Tablet/39092206
Walmart,http://www.walmart.com/ip/Nextbook-10.1-Intel-Quad-Core-2-In-1-Detachable-Windows-8.1-Tablet/39092206
I'm having trouble writing that price to the adjacent column C (header = price) in the same CSV.
require 'nokogiri'
require 'open-uri'
require 'csv'
contents = CSV.open "mp_lookup.csv", headers: true, header_converters: :symbol
contents.each do |row|
row_url = row[:url]
goto_url = Nokogiri::HTML(open(row_url))
new_price = goto_url.css('meta[itemprop="price"]')[0]['content']
#----
#In this section, I'm looking to write the value of new_price to the 3rd column in the same CSV file
#----
end
In the past, I've been able to use:
in_file = open("mp_lookup.csv", 'w')
in_file.write(new_price)
But this doesn't seem to work in this situation.
Any help is appreciated!
The simple answer is that you can refer to the :price column in the CSV file, just like you refer to the :url column. Try this code to set the price in the CSV object in memory:
row[:price] = new_price
After you've read through all of the records, you'll want to save the CSV file again. You can save it to any filename, but we'll simply overwrite the previous file in this example:
CSV.open("mp_lookup.csv", "wb") do |csv|
contents.each do |row|
csv << row
end
end
In a real production environment, you'd want to be more fault tolerant than this, and preserve the original file until the end of the process. However, this shows how to update the values in the price column for each row, and then save the changes to a file.

Concatenate array and string for CSV

I'm attempting to concatenate a string to an array value. Since the URL/domain is the same, I simply store the users email prefix and append the url/domain. I need to export the full email address out to CSV:
CSV.generate(options) do |csv|
columns = %w(name, email_address)
url = "#example.com"
all.each do |location|
csv << location.attributes.values_at(*columns) + [url]
end
end
Currently the resulting output is:
Joe, user1, #example.com
Bob, user2, #example.com
What I need is:
Joe, user1#example.com
Bob, user2#example.com
How can I achieve the above?
location is a model object, right?
CSV.generate(options) do |csv|
domain = "example.com"
all.each do |location|
csv << [location.name, "#{location.email_address}##{domain}"]
end
end
Update:
IMO that's cleaner as well. But if you want to keep your version, then I suggest you create a full_email_address method in your Location model which returns something like username#domain.com.
Then, you can vary the columns data later on and easily modify your CSV output. Like so:
class Location << ActiveRecord::Base
def full_email_address
return "" if self.email_address.blank?
domain = "example.com" # or save this as a constant in the class
"#{self.email_address}##{domain}"
end
end
CSV.generate(options) do |csv|
columns = %w{name full_email_address} # add other methods or attributes here
all.each do |location|
csv << columns.map{ |moa| location.public_send(moa) }
end
end
Try this:
CSV.generate(options) do |csv|
columns = %w(name, email_address)
url = "#example.com"
all.each do |location|
row = location.attributes.values_at(*columns)
row[-1] = row[-1] + url
csv << row
end
end
Currently the array written to CSV is ['Joe', 'user1'] + ['#example.com'], so instead of adding url to the attributes array I am adding it to the last attribute.

Reading every line in a CSV and using it to query an API

I have the following Ruby code:
require 'octokit.rb'
require 'csv.rb'
CSV.foreach("actors.csv") do |row|
CSV.open("node_attributes.csv", "wb") do |csv|
csv << [Octokit.user "userid"]
end
end
I have a csv called actors.csv where every row has one entry - a string with a userid.
I want to go through all the rows, and for each row do Octokit.user "userid", and then store the output from each query on a separate row in a CSV - node_attributes.csv.
My code does not seem to do this? How can I modify it to make this work?
require 'csv'
DOC = 'actors.csv'
DOD = 'new_output.csv'
holder = CSV.read(DOC)
You can navigate it by calling
holder[0][0]
=> data in the array
holder[1][0]
=> moar data in array
make sense?
#make this a loop
profile = []
profile[0] = holder[0][0]
profile[1] = holder[1][0]
profile[2] = 'whatever it is you want to store in the new cell'
CSV.open(DOD, "a") do |data|
data << profile.map
end
#end the loop here
That last bit of code will print whatever you want into a new csv file

Resources