Utilizing data from a csv file

Hi I basically want to create a function that takes in data that has
been taken in from a csv file.

So I have this on my main file…

songs = reader.read_in_songs(song_csv_file_name)

puts “\nBuilding Libraries…”
libs = Song.build_all(songs)


here is the song class…

class Song
attr_accessor :name, :owner
def initialize(name, owner)
@name = name
@owner = owner
end

def to_s
puts " #{@name} #{@owner}"
end

def self.build_all(songs)

I want to be able to ‘create song objects’(?) from the data taken in

from the csv file here

end

end


Here is a rough idea I have so far, but it’s returning nothing

songs = []

songs.each {|song| songs << Song.new(song.name, song.owner)}

Do I have the right idea?

On Sun, Oct 24, 2010 at 7:34 AM, Paul R. [email protected]
wrote:

Here is a rough idea I have so far, but it’s returning nothing

No kidding :slight_smile:

songs = []
songs.each {|song| songs << Song.new(song.name, song.owner)}

Do I have the right idea?

:~$ irb

songs = []
=> []
songs.each {|song| puts song }
=> []

What exactly would you expect to get out of an empty array?

Hassan S. wrote in post #956709:

On Sun, Oct 24, 2010 at 7:34 AM, Paul R. [email protected]
wrote:

Here is a rough idea I have so far, but it’s returning nothing

No kidding :slight_smile:

songs = []
songs.each {|song| songs << Song.new(song.name, song.owner)}

Do I have the right idea?

:~$ irb

songs = []
=> []
songs.each {|song| puts song }
=> []

What exactly would you expect to get out of an empty array?

Sure, scrap that :slight_smile:
If I put Song.new(songs.name, songs.id) into this function I get
something. Any idea how I can ‘create objects’(?) from the data from the
csv file?

On Sun, Oct 24, 2010 at 8:27 AM, Paul R. [email protected]
wrote:

If I put Song.new(songs.name, songs.id) into this function I get
something. Any idea how I can ‘create objects’(?) from the data from the
csv file?

I’m not sure I understand the question; Song.new creates an object.

Are you having trouble with the actual values? What does this function
reader.read_in_songs(song_csv_file_name)

actually return? Can you show an example?

Sure, here’s the csv file…

“Songname”,“owner”
##################
“Superfast Jellyfish”,“mick”
“On Melancholy Hill”,“ken”
######################

I have a reader class that successfully reads the csv file.

p songs = reader.read_in_songs(song_csv_file_name)

returns this…

Superfast Jellyfish mick
On Melancholy Hill ken
[, , ]

I then want to call…

Song.build_all(songs)

I want this method to grab the data in ‘songs’.

I want to implement build_all in Song class to do this

Incidentally, this does what I want to achieve

def self.build_all(songs)

songs = ObjectSpace.each_object(Song).to_a

end

but I’d rather keep away from ObjectSpace

On Sun, Oct 24, 2010 at 9:50 AM, Paul R. [email protected]
wrote:

returns this…

Superfast Jellyfish mick
On Melancholy Hill ken

?! So, I’d say you need to work on that – there’s no obvious way to
split either of those into 2 fields.

I would think you’d want to return something per line like e.g.

{:owner=>“mick”, :songname=>“Superfast Jellyfish”}

which you could then pass to Song.new

On 10/24/2010 09:34 AM, Paul R. wrote:


end

end


Here is a rough idea I have so far, but it’s returning nothing

songs = []

songs.each {|song| songs << Song.new(song.name, song.owner)}

This rightly does nothing. The each method operates over the array
itself by calling the given block with each of the array elements in
turn. In this case, songs is an empty array because you set it as such
just before calling songs.each, so there are no elements for the each
method to process.

You seem to have a small gap in your understanding of how variables are
assigned values here. When you set songs to [], you have lost the value
passed in as the songs argument of the build_all method. Do you see
what I mean?

Try something even simpler:

def make_array(items)
new_items = []
items.each { |item| new_items << (item * 10) }
new_items
end

these_items = [1, 2, 3, 4]
those_items = make_array(these_items)

After running this, what are the values of these_items and those_items?
How does that change if you replace “new_items” with “items” everywhere
in the make_array function? Why do you think that happens? From where
is the value of “item” acquired in the make_array function? What
happens if you change these_items as follows:

these_items = [‘1’, ‘2’, ‘3’, ‘4’]

-Jeremy

On 10/24/2010 12:56 PM, Paul R. wrote:

Incidentally, this does what I want to achieve

def self.build_all(songs)

songs = ObjectSpace.each_object(Song).to_a

end

Aside from the mess of using ObjectSpace like this, for what reason do
you pass in a value for songs in the first place? :wink:

-Jeremy

Jeremy B. wrote in post #956736:

On 10/24/2010 09:34 AM, Paul R. wrote:


end

end


Here is a rough idea I have so far, but it’s returning nothing

songs = []

songs.each {|song| songs << Song.new(song.name, song.owner)}

This rightly does nothing. The each method operates over the array
itself by calling the given block with each of the array elements in
turn. In this case, songs is an empty array because you set it as such
just before calling songs.each, so there are no elements for the each
method to process.

You seem to have a small gap in your understanding of how variables are
assigned values here. When you set songs to [], you have lost the value
passed in as the songs argument of the build_all method. Do you see
what I mean?

Try something even simpler:

def make_array(items)
new_items = []
items.each { |item| new_items << (item * 10) }
new_items
end

these_items = [1, 2, 3, 4]
those_items = make_array(these_items)

After running this, what are the values of these_items and those_items?
How does that change if you replace “new_items” with “items” everywhere
in the make_array function? Why do you think that happens? From where
is the value of “item” acquired in the make_array function? What
happens if you change these_items as follows:

these_items = [‘1’, ‘2’, ‘3’, ‘4’]

-Jeremy

Thanks Jermey, this example has helped. This did the trick…

def self.build_all(songs)

new_items = []
songs.each { |item| new_items << Song.new(item.name, item.owner) }
new_items

end

Paul R. wrote in post #956704:

Hi I basically want to create a function that takes in data that has
been taken in from a csv file.

Hi Paul,

It looks like you have a handle on your data structure but need help
parsing the data from your csv file. I wrote a short program do handle
data from csv files a little while back. Maybe looking it over will help
you with your own project.

Code:

filename = ‘data.csv’
grades = []
heading = []
values = []
i = 0

file = File.new(filename, ‘r’)

file.each_line("\n") do |row|
columns = row.split(",")
#p columns
grades << columns
end

grades.each do |line|
heading = line if i == 0
values = line if i == 1
if i > 2
# Print Unit title and student name
print values[0], “\nName: “, line[0],”,”, line[1],"\n\n"

# print total score and final grade
print "Total Score: ", line[2], " -- ", line[3], "\n\n"

# print breakdown HOURS
print heading[4], ":         ", line[5], " of ", values[4], "\n"

# print breakdown PARTICIPATION
print heading[5], ":     ",line[6], "% of ", values[5], "\n"

# print breakdown Assignment Points
print heading[6], ":         ",line[7], " of ", values[6], " total 

points\n"

# print breakdown Assignments
print heading[7], ":     ",line[8], "% of ", values[7], "\n"

# print breakdown WA Law
print heading[8], ":              ",line[9], "% of ", values[8], 

“\n”

# print breakdown Quiz
print heading[9], ":                    ",line[10], "% of ", 

values[9], “\n”

# print breakdown Exam
print heading[10], ":                  ",line[11], "% of ", 

values[10], “\n”

# print breakdown EC
print heading[11].chomp, ":                   ",line[12], "\n\n\n\n"


#print line

end
i += 1
end

END CODE

It’s a little ugly with the formating. I wasn’t planning on showing this
to anyone when I wrote it. It reads the csv file line by line and then
based on the cell position it stores the data in an array. You should be
able to substitute my arrays with your class objects no problem.

Hope this helps you.

Greg-

Jeremy B. wrote in post #956737:

On 10/24/2010 12:56 PM, Paul R. wrote:

Incidentally, this does what I want to achieve

def self.build_all(songs)

songs = ObjectSpace.each_object(Song).to_a

end

Aside from the mess of using ObjectSpace like this, for what reason do
you pass in a value for songs in the first place? :wink:

-Jeremy

Yeah good point.

I don’t have my thinking cap on with this ruby stuff :slight_smile:

Greg B. wrote in post #956738:

Paul R. wrote in post #956704:

Hi I basically want to create a function that takes in data that has
been taken in from a csv file.

Hi Paul,

It looks like you have a handle on your data structure but need help
parsing the data from your csv file. I wrote a short program do handle
data from csv files a little while back. Maybe looking it over will help
you with your own project.

Thanks Greg, much appreciated

On 10/24/2010 01:10 PM, Paul R. wrote:

those_items = make_array(these_items)
Thanks Jermey, this example has helped. This did the trick…

def self.build_all(songs)

new_items = []
songs.each { |item| new_items << Song.new(item.name, item.owner) }
new_items

end

You’re farther along, but you’re still missing something important that
would allow you to reduce this further. What are the kinds of items in
the songs list you pass into the build_all method? Are they arrays of
strings, numbers, or something else?

-Jeremy

You’re farther along, but you’re still missing something important that
would allow you to reduce this further. What are the kinds of items in
the songs list you pass into the build_all method? Are they arrays of
strings, numbers, or something else?

-Jeremy

The larger implementation of this will have strings and numbers

On 10/24/2010 02:14 PM, Paul R. wrote:

You’re farther along, but you’re still missing something important that
would allow you to reduce this further. What are the kinds of items in
the songs list you pass into the build_all method? Are they arrays of
strings, numbers, or something else?

-Jeremy

The larger implementation of this will have strings and numbers

Am I correct that the answer to my question is that you are passing an
array of Song instances to the build_all method? If so, what are you
planning to do that would change that?

I remember from your earlier code example from the earlier thread that
you seem to be trying to define per-user libraries of some kind where
you have one CSV file with a list of songs and another CSV file with a
list of people. IIRC, your implementation there was trying to link the
two lists via an ID column shared between the two CSV files.

If this is your goal, I don’t understand how this will change your
method of loading the songs CSV file. Yeah, you’ll add some fields to
your Song implementation, but that shouldn’t require you to move the
instantiation code into a class method of Song any more than your
current implementation does.

-Jeremy

Having build_all in Song class was me just trying to see if I could read
data with the build_all method.

You’re right the old implementation you referred to is what I will be
working with. This features as you mentioned linking the two lists. I’m
just going to try and get myself out of this mudddle.

So to be specific… Here’s the original build_all. This is in the
library class (there wil be no build_all method in Song)

(ignore the !)

def self.build_all
lib_objs = ObjectSpace.each_object(Library).to_a
! ! songs = ObjectSpace.each_object(Song).to_a
! ! lib_objs.each do |library|
! ! ! songs.each do |song|
! ! ! ! if song.in_libs.include?(library)
! ! ! ! ! then library.songs << song end
! ! ! end
! ! ! end
lib_objs
end

Here is part of the new build_all method…

def self.build_all(lib, songs)

new_items_lib = []
lib.each { |iteml| new_items_lib << Library.new(iteml.name,
iteml.songx) }

new_items_song = []
songs.each { |item| new_items_song << Song.new(item.name, item.album,
item.artist, item.time, item.year, item.id, item.in_libs) }

It does what I want it to do for ‘songs’, but throws up an error for
‘lib’. I’m going to take a few steps back on this

On 10/24/2010 3:18 PM, Paul R. wrote:

Having build_all in Song class was me just trying to see if I could read
data with the build_all method.

You’re right the old implementation you referred to is what I will be
working with. This features as you mentioned linking the two lists. I’m
just going to try and get myself out of this mudddle.

FYI, what you’re trying to do with CSV files is often handled with a
relational database. Have you considered trying out sqlite? There are
some gems such as ActiveRecord that can make using that database backend
and others relatively easy.

songs.each { |item| new_items_song << Song.new(item.name, item.album,
item.artist, item.time, item.year, item.id, item.in_libs) }

It does what I want it to do for ‘songs’, but throws up an error for
‘lib’. I’m going to take a few steps back on this

If you want some help with that, we’ll need to know the details of the
error. If you’re just stating what your working on next though, good
luck. :slight_smile:

-Jeremy

On Sun, Oct 24, 2010 at 8:04 PM, Greg B. [email protected]
wrote:

you with your own project.
file = File.new(filename, ‘r’)

file.each_line(“\n”) do |row|
columns = row.split(“,”)

Split is simple, but it doesn’t deal with CSV rows which have strings
with commas in them. Like this:
apple, pear, “banana, salad”, apricot

The FasterCSV gem is an excellent way to read and write CSV files and
it deals with these sorts of problems: http://fastercsv.rubyforge.org

On Mon, Oct 25, 2010 at 4:50 PM, Leslie V.
[email protected] wrote:

The FasterCSV gem is an excellent way to read and write CSV files and
it deals with these sorts of problems: http://fastercsv.rubyforge.org

It’s also been moved into Ruby’s core API with 1.9 (require “csv”).
Docs are here: http://ruby-doc.org/ruby-1.9/classes/CSV.html

P.S.: This makes a good case why frames are Evil, too. :wink:


Phillip G.

Though the folk I have met,
(Ah, how soon!) they forget
When I’ve moved on to some other place,
There may be one or two,
When I’ve played and passed through,
Who’ll remember my song or my face.