1

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.

2
  • 1
    I think you need to add format: :csv to link_to Commented Mar 7, 2017 at 21:14
  • Yes you are right. This allowed the button to be clicked, and the csv to download immediately after running the back-end code. Thank you! link_to "Download Employee CSV", download_employee_csv_path(format: :csv) Commented Mar 7, 2017 at 21:28

2 Answers 2

3

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
Sign up to request clarification or add additional context in comments.

Comments

2

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] }

1 Comment

csv << attributes.map{ |attr| obj[attr.to_sym] } did the trick! Thank you sir, I am most gracious.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.