Reputation: 1255
I have an array of objects. I am trying to create CSV data and allow the user to download that file but I get the following error:
Undefined method 'first_name' for Hash:0x007f946fc76590
employee_csv_data.each do |obj|
csv << attributes.map{ |attr| obj.send(attr) }
end
end
end
This is the button that allows a user to download the CSV:
<%= link_to "Download Employee CSV", download_employee_csv_path %>
Controller:
def download_employee_csv
employee_csv_data = []
employees.each do |employee|
employee_csv_data << {
first_name: employee[:first_name],
last_name: employee[:last_name],
email: employee_email,
phone1: employee[:phone1],
gender: employee[:gender],
veteran: employee[:veteran].to_s,
dob: employee[:dob],
core_score: service_score,
performance_rank: rank,
industry_modules_passed: industry_modules_passed
}
end
respond_to do |format|
format.html
format.csv { send_data Employer.to_csv(employee_csv_data), filename: "download_employee_csv.csv" }
end
end
employee_csv_data:
=> [{:first_name=>"Christopher",
:last_name=>"Pelnar",
:email=>"[email protected]",
:phone1=>"4072422433",
:gender=>"male",
:veteran=>"true",
:dob=>"1988-09-09",
:core_score=>"No Score",
:performance_rank=>"No Rank",
:industry_modules_passed=>"No Industry Modules Passed"},
{:first_name=>"chris",
:last_name=>"pelnar",
:email=>"[email protected]",
:phone1=>"4072422433",
:gender=>"male",
:veteran=>"true",
:dob=>"1998-09-09",
:core_score=>"729",
:performance_rank=>"Good",
:industry_modules_passed=>"Entry-Service, Entry-Tech"}]
Model:
def self.to_csv(employee_csv_data)
attributes = %w(first_name last_name email phone gender veteran dob core_score performance_rank industry_modules_passed)
CSV.generate(headers: true) do |csv|
csv << attributes
employee_csv_data.each do |obj|
csv << attributes.map{ |attr| obj.send(attr) }
end
end
end
When I click the button, it takes me to the blank HTML page without any problem. When I add .csv
to the filename in the URL on that page I get the error.
Upvotes: 1
Views: 2618
Reputation: 181
I adapted @Ctpelnar1988's answer to determine the attributes dynamically and allow each array item to have different columns:
def array_of_hashes_to_csv(array)
array_keys = array.map(&:keys).flatten.uniq
CSV.generate(headers: true) do |csv|
csv << array_keys
array.each do |obj|
csv << array_keys.map{ |attr| obj[attr] }
end
end
end
Example:
puts array_of_hashes_to_csv([
{attr_a: 1, attr_b: 2},
{attr_a: 3, attr_c: 4}
])
attr_a,attr_b,attr_c
1,2,
3,,4
In the more specific "employee_csv_data" context, I think it'd look like this:
def self.to_csv(employee_csv_data)
attributes = employee_csv_data.map(&:keys).flatten.uniq
CSV.generate(headers: true) do |csv|
csv << attributes
employee_csv_data.each do |obj|
csv << attributes.map { |attr| obj[attr] }
end
end
end
Upvotes: 3
Reputation: 3255
It looks like it's an array of Hashes. To access properties of a hash in Ruby you need to use brackets. Try updating your code to this:
csv << attributes.map{ |attr| obj.send([], attr) }
or more concisely:
csv << attributes.map{ |attr| obj[attr] }
One more thing, in the example you provided, the keys in the hash are symbols which means you may need to convert your attributes to symbols when trying to access them, like this:
csv << attributes.map{ |attr| obj[attr.to_sym] }
Upvotes: 2