1

I want to add new column and update existing values in CSV response. How can I do simpler and better way of doing the below transformations?

Input

id,name,country
1,John,US
2,Jack,UK
3,Sam,UK

I am using following method to parse the csv string and add new column

# Parse original CSV
rows = CSV.parse(csv_string, headers: true).collect do |row|
  hash = row.to_hash
  # Merge additional data as a hash.
  hash.merge('email' => '[email protected]')
end

# Extract column names from first row of data
column_names = rows.first.keys
# Generate CSV after transformation of csv
csv_response = CSV.generate do |csv|
  csv << column_names
  rows.each do |row|
    # Extract values for row of data
    csv << row.values_at(*column_names)
  end
end

I am using following method to parse the csv and update existing values

name_hash = {"John" => "Johnny", "Jack" => "Jackie"}

Parse original CSV

rows = CSV.parse(csv_string, headers: true).collect do |row|
  hash = row.to_hash
  hash['name'] = name_hash[hash['name']] if name_hash[hash['name']] != nil
  hash
end

# Extract column names from first row of data
column_names = rows.first.keys
# Generate CSV after transformation of csv
csv_response = CSV.generate do |csv|
  csv << column_names
  rows.each do |row|
    # Extract values for row of data
    csv << row.values_at(*column_names)
  end
end

1 Answer 1

1

One possible option given the following reference data to be used for modifying the table:

name_hash = {"John" => "Johnny", "Jack" => "Jackie"}
sample_email = {'email' => '[email protected]'}

Just store in rows the table converted to hash:

rows = CSV.parse(csv_string, headers: true).map(&:to_h)
#=> [{"id"=>"1", "name"=>"John", "country"=>"US"}, {"id"=>"2", "name"=>"Jack", "country"=>"UK"}, {"id"=>"3", "name"=>"Sam", "country"=>"UK"}]


Then modify the hash based on reference data (I used Object#then for Ruby 2.6.1 alias of Object#yield_self for Ruby 2.5):

rows.each { |h| h.merge!(sample_email).then {|h| h['name'] = name_hash[h['name']] if name_hash[h['name']] } }
#=> [{"id"=>"1", "name"=>"Johnny", "country"=>"US", "email"=>"[email protected]"}, {"id"=>"2", "name"=>"Jackie", "country"=>"UK", "email"=>"[email protected]"}, {"id"=>"3", "name"=>"Sam", "country"=>"UK", "email"=>"[email protected]"}]


Finally restore the table:

csv_response = CSV.generate(headers: rows.first.keys) { |csv| rows.map(&:values).each { |v| csv << v } }

So you now have:

puts csv_response

# id,name,country,email
# 1,Johnny,US,[email protected]
# 2,Jackie,UK,[email protected]
# 3,Sam,UK,[email protected]
Sign up to request clarification or add additional context in comments.

1 Comment

Will your solution gives better performance than above method posted in question? I want to process the csv in better way with simpler code.

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.