Rails + Spreadsheet gem: How to push row into sheet without index? - ruby-on-rails

I have to loop through an array and I'm not sure which row I'm going to start at since that varies:
def add_data
#sheet = #workbook.create_worksheet
responses.each do |response|
# I want something like #sheet.rows << [response.id]
end
end
I'm looking through the docs but all the examples given use a specified row index: http://spreadsheet.rubyforge.org/GUIDE_txt.html
How would I push an array of values to the next row in the file?

Just dug into the classes themselves and came up with a solution... the documentation kinda whomps:
#sheet.insert_row(#sheet.last_row_index + 1, ["hey", "cool"])

Related

Iterating through CSV::Rows

I'm going to preface that I'm still learning ruby.
I'm writing a script to parse a .csv and identify possible duplicate records in the data-set.
I have a .csv file with headers, so I'm parsing the data so that I can access each row using a header title as such:
#contact_table = CSV.parse(File.read("app/data/file.csv"), headers: true)
# Prints all last names in table
puts contact_table['last_name']
I'm trying to iterate over each row in the table and identify if the last name I'm currently iterating over is similar to the next last name, but I'm having trouble doing this. I guess the way I'm handling it is as if it's an array, but I checked the type and it's a CSV::Row.
example (this doesn't work):
#contact_table.each_with_index do |c, i|
puts "first contact is #{c['last_name']}, second contact is #{c[i + 1]['last_name']}"
end
I realized this doesn't work like this because the table isn't an array, it's a CSV::Row like I previously mentioned. Is there any method that can achieve this? I'm really blanking right now.
My csv looks something like this:
id,first_name,last_name,company,email,address1,address2,zip,city,state_long,state,phone
1,Donalt,Canter,Gottlieb Group,dcanter0#nydailynews.com,9 Homewood Alley,,50335,Des Moines,Iowa,IA,515-601-4495
2,Daphene,McArthur,"West, Schimmel and Rath",dmcarthur1#twitter.com,43 Grover Parkway,,30311,Atlanta,Georgia,GA,770-271-7837
#contact_table should be a CSV::Table which is a collection of CSV::Rows so in this:
#contact_table.each_with_index do |c, i|
...
end
c is a CSV::Row. That's why c['last_name'] works. The problem is that here:
c[i + 1]['last_name']
you're looking at c (a single row) instead of #contact_table, if you said:
#contact_table[i + 1]['last_name']
then you'd get the next last name or, when c is the last row, an exception because #contact_table[i+1] will be nil.
Also, inside the iteration, c is the current (or (i+1)th) row and won't always be the first.
What is your use case for this? Seems like a school project?
I recommend for_each instead of parse (see this comparison). I would probably use a Set for this.
Create a Set outside of the scope of parsing the file (i.e., above the parsing code). Let's call it rows.
Call rows.include?(row) during each iteration while parsing the file
If true, then you know you have a duplicate
If false, then call rows.add(row) to add the new row to the set
You could also just fill your set with an individual value from a column that must be distinct (e.g., row.field(:some_column_name)), such as email or phone number, and do the same inclusion check for that.
(If this is for a real app, please don't do this. Use model validations instead.)
I would use #read instead of #parse and do something like this:
require 'csv'
LASTNAME_INDEX = 2
data = CSV.read('data.csv')
data[1..-1].each_with_index do |row, index|
puts "Contact number #{index + 1} has the following last name : #{row[LASTNAME_INDEX]}"
end
#~> Contact number 1 has the following last name : Canter
#~> Contact number 2 has the following last name : McArthur

ruby each.with_index - reflect input

I have looked around for info on how to do this, just can't quite get it myself. Fairly new to Ruby and I'm building a gem. I can return a list of results from a website ok with Nokogiri.
The issue I'm having is how to take an input number and relate it to another piece of text from the website I'm scraping. So you pick a movie title by number from a list and then you return the relevant movie outline. Maybe I don't want to iterate through all the objects (again) at all.
This is what I'm looking at:
def menu
input = nil
while input != "exit"
input = gets.strip.downcase
if input.to_i < 24
#movies.each.with_index(1) do |movie, i|
puts "Description: #{movie.outline}"
end
end
end
end
So we create a variable for input. While the input is not the word exit and less than the number 24, we iterate through the movies and put the relevant one by index number. Currently putting ALL the movie outlines so I feel like I should ditch the iteration. I've tried a number of things around adding the input to movie.outline...
Any help or hints would be great!
It's hard to say for sure, but, are you maybe just trying to do this?
input = gets.to_i
if input < 24
movie = #movies[input]
puts "Description: #{movie.outline}"
end
Or something to that effect?
If you want to access an Array element, you use the [] notation (I'm assuming #movies is an Array, but if it's some other enumerable, you'll need to tell us what that is).

rails each loop with from to

In my controller i fetch 9 row's for object organizations.
#organizations = Organization.where(parent_id: 1).order(city_id: :asc, is_vip: :desc, title: :asc).limit(25).sample(9)
and then in view i must separate this 9 value's to 3 view loops, like first .each do if for row's 1-3, second for 4-6, third 6-9
and i try so:
- #organizations[0..2].each do |org|
...
- #organizations[3..5].each do |org|
...
- #organizations[6..8].each do |org|
...
but it seems that i do something wrong, but what exactly? and how to do it right?
Not sure why your data is duplicated. But you can use the following method for splitting the array into slices
you can use each_slice
#organization.each_slice(3) do |sliced_orgs|
end
Some documentation here
First I don't really get why you use .limit(25).sample(9), you could limit your results to 9 already. But maybe you have some use of the random factor introduced by sample? Strange.
Other than that,
#organizations[0..2].each do |org|
puts org
end
...
should work perfectly fine. If the data is repeated it is because you have multiple times the same entry in your model. sample(9) is taking random unique entries and #organizations[0..2] is a fixed range returning an array or nil. (Rubydoc : ary[range] → new_ary or nil)
In short, nothing wrong with the code but probably somewhere in your data/logic.

How do I populate a table from an Excel Spreadsheet in Rails?

I have a simple 4-column Excel spreadsheet that matches universities to their ID codes for lookup purposes. The file is pretty big (300k).
I need to come up with a way to turn this data into a populated table in my Rails app. The catch is that this is a document that is updated now and then, so it can't just be a one-time solution. Ideally, it would be some sort of ruby script that would read the file and create the entries automatically so that when we get emailed a new version, we can just update it automatically. I'm on Heroku if that matters at all.
How can I accomplish something like this?
If you can, save the spreadsheet as CSV, there's much better gems for parsing CSV files than for parsing excel spreadsheets. I found an effective way of handling this kind of problem is to make a rake task that reads the CSV file and creates all the records as appropriate.
So for example, here's how to read all the lines from a file using the old, but still effective FasterCSV gem
data = FasterCSV.read('lib/tasks/data.csv')
columns = data.remove(0)
unique_column_index = -1#The index of a column that's always unique per row in the spreadsheet
data.each do | row |
r = Record.find_or_initialize_by_unique_column(row[unique_column_index])
columns.each_with_index do | index, column_name |
r[column_name] = row[index]
end
r.save! rescue => e Rails.logger.error("Failed to save #{r.inspect}")
end
It does kinda rely on you having a unique column in the original spreadsheet to go off though.
If you put that into a rake task, you can then wire it into you're Capistrano deploy script, so it'll be run every time you deploy. the find_or_initialize should ensure you shouldn't get duplicate records.
Parsing newish Excel files isn't too much trouble using Hpricot. This will give you a two-dimensional array:
require 'hpricot'
doc = open("data.xlsx") { |f| Hpricot(f) }
rows = doc.search('row')
rows = rows[1..rows.length] # Skips the header row
rows = rows.map do |row|
columns = []
row.search('cell').each do |cell|
# Excel stores cell indexes rather than blank cells
next_index = (cell.attributes['ss:Index']) ? (cell.attributes['ss:Index'].to_i - 1) : columns.length
columns[next_index] = cell.search('data').inner_html
end
columns
end

FasterCSV: columns into an array -- rails

I'm trying to get fastercsv setup so that rather than parsing each row, it will place each column into an multi array.
CSV import file:
id, first name, last name, age
1, joe, smith, 11
2, jane, doe, 14
Save to array named people:
people[0][0] would equal id
people[2][1] would equal jane
This is what I currently have:
url = 'http://url.com/file.csv'
open(url) do |f|
f.each_line do |line|
FasterCSV.parse(line) do |row|
row
end
end
end
Any help is appreciated.
Have you read the FasterCSV documentation?
If you did, you would know that the easiest way to do what you want is:
people = FasterCSV.read('http://url.com/file.csv')
Thanks EmFi, with your help I was able to come up with a solution.
This takes a remote url csv file and loads it into a multi-dimensional array, based on columns.
require 'rio'
require 'fastercsv'
url = 'http://remoteurl.com/file.csv'
people = FasterCSV.parse(rio(url).read)
You can use CsvMapper on top of FasterCSV
It s a life saver

Resources