Ash Pettit
Ash Pettit

Reputation: 457

Using loops within CSV export - Ruby

I'm trying to DRY up my code and wondering if many people have experience with CSV's and ruby.

My code is below. It works just... Its awful.

I'm wondering if anyone has any ideas on how I could do the following:

1 - How could I use a loop rather than explicit 1..10 that I've done. I did try a few ways but couldn't get them to work with CSV. 2 - Is there a nicer way to do headers in CSV? 3 - Any other ideas on how to make CSV code nicer?

I initially went with this

(1..10).each do |number| 
end 

However the csv system didn't like that one! It was thinking my end statements were incorrect however, I don't think this was the case.

Here's my code. If you have any bright ideas you're awesome! Yes I know it's awful, just wondering how I could do it better!

require 'csv'

class CampagignsCsv
  class << self
    HEADERS = [
      'Job Title',
      'Business Name',
      'Business Contact Name',
      'Location',
      'Job Status',
      'Created date',
      'Last Modified date',
      '# Positions',
      'Description',
      'Std/Prem',
      'Referral code (To discuss)',
      'Coupon code (To discuss)',
      'Question1',
      'Knockout?1',
      'Correct Answer1',
      'Question2',
      'Knockout?2',
      'Correct Answer2',
      'Question3',
      'Knockout?3',
      'Correct Answer3',
      'Question4',
      'Knockout?4',
      'Correct Answer4',
      'Question5',
      'Knockout?5',
      'Correct Answer5',
      'Question6',
      'Knockout?6',
      'Correct Answer6',
      'Question7',
      'Knockout?7',
      'Correct Answer7',
      'Question8',
      'Knockout?8',
      'Correct Answer8',
      'Question9',
      'Knockout?9',
      'Correct Answer9',
      'Question10',
      'Knockout?10',
      'Correct Answer10'
    ].freeze

    def report
      puts 'campaigns_report.csv created in reporting_output folder'
      CSV.open("reporting_output/campagins_report.csv", "wb") do |csv|
        csv << HEADERS
        Paddl::Models::Job.all.each do |job|
          csv << [
            job.title,
            job.employer.business_name,
            job.employer.profile.full_name,
            job.address,
            job.status,
            job.created_at,
            job.updated_at,
            job.num_of_positions,
            job.description,
            job.employer.account_type,
            'null',
            'null',
            job.job_questions.map { |item| item[:question] }[1],
            job.job_questions.map { |item| item[:knockout] }[1],
            job.job_questions.map { |item| item[:correct_answer] }[1],
            job.job_questions.map { |item| item[:question] }[2],
            job.job_questions.map { |item| item[:knockout] }[2],
            job.job_questions.map { |item| item[:correct_answer] }[2],
            job.job_questions.map { |item| item[:question] }[3],
            job.job_questions.map { |item| item[:knockout] }[3],
            job.job_questions.map { |item| item[:correct_answer] }[3],
            job.job_questions.map { |item| item[:question] }[4],
            job.job_questions.map { |item| item[:knockout] }[4],
            job.job_questions.map { |item| item[:correct_answer] }[4],
            job.job_questions.map { |item| item[:question] }[5],
            job.job_questions.map { |item| item[:knockout] }[5],
            job.job_questions.map { |item| item[:correct_answer] }[5],
            job.job_questions.map { |item| item[:question] }[6],
            job.job_questions.map { |item| item[:knockout] }[6],
            job.job_questions.map { |item| item[:correct_answer] }[6],
            job.job_questions.map { |item| item[:question] }[7],
            job.job_questions.map { |item| item[:knockout] }[7],
            job.job_questions.map { |item| item[:correct_answer] }[7],
            job.job_questions.map { |item| item[:question] }[8],
            job.job_questions.map { |item| item[:knockout] }[8],
            job.job_questions.map { |item| item[:correct_answer] }[8],
            job.job_questions.map { |item| item[:question] }[9],
            job.job_questions.map { |item| item[:knockout] }[9],
            job.job_questions.map { |item| item[:correct_answer] }[9],
            job.job_questions.map { |item| item[:question] }[10],
            job.job_questions.map { |item| item[:knockout] }[10],
            job.job_questions.map { |item| item[:correct_answer] }[10]
          ]
        end
      end
    end
  end
end

Upvotes: 0

Views: 79

Answers (2)

tadman
tadman

Reputation: 211600

Here's an approach that organizes things better, plus cuts way down on duplication:

require 'csv'

class CampagignsCsv
  HEADERS = [
    'Job Title',
    'Business Name',
    'Business Contact Name',
    'Location',
    'Job Status',
    'Created date',
    'Last Modified date',
    '# Positions',
    'Description',
    'Std/Prem',
    'Referral code (To discuss)',
    'Coupon code (To discuss)'
  ] + (1..10).flat_map do |n|
    "Question#{n}",
    "Knockout?#{n}",
    "Correct Answer#{n}",
  end

  SECTIONS = [
    :question,
    :knockout,
    :correct_answer
  ]

  def self.report
    puts 'campaigns_report.csv created in reporting_output folder'

    CSV.open("reporting_output/campagins_report.csv", "wb") do |csv|
      csv << HEADERS

      Paddl::Models::Job.all.each do |job|
        csv << [
          job.title,
          job.employer.business_name,
          job.employer.profile.full_name,
          job.address,
          job.status,
          job.created_at,
          job.updated_at,
          job.num_of_positions,
          job.description,
          job.employer.account_type,
          'null',
          'null'
        ] + SECTIONS.flat_map do |section|
          (1..10).map do |n|
            job.job_questions[section][n]
          end
        end
      end
    end
  end
end

Normally you declare constants at the very top level of your class, not inside a class << self block. That's also not necessary since declaring a methods as self.x automatically makes it a class method.

Using flat_map can help convert array-of-array type results to a flat array, very handy in this case.

Upvotes: 1

pocari
pocari

Reputation: 1161

How's this?

...
job.employer.account_type,
'null',
'null',
*1.upto(10).flat_map {|i|
  jq = job.job_questions[i]
  [jq[:question], jq[:knockout], jq[:correct_answer]]
}
...

Upvotes: 1

Related Questions