On Wed, May 4, 2011 at 6:34 PM, Jolyon R. <jolyonruss / gmail.com> wrote:
> Hey guys,
>
> I'm day 3 into The Pragmatic Programmers - Seven Languages in Seven
> Days, having previously done a little ruby. I'm from an Actionscript
> background and finding Ruby a little challenging, so bear with me.

Welcome to the wonderful world of Ruby!

> I've been parsing some very simple CSV, I've seen there are libraries
> out there to handle this, but the book is about learning.
>
> The book gives us some simple code and the task is to elaborate on it:
>
> module ActsAsCsv
>
> =A0def self.included(base)
> =A0 =A0base.extend ClassMethods
> =A0end
>
> =A0module ClassMethods
> =A0 =A0def acts_as_csv
> =A0 =A0 =A0include InstanceMethods
> =A0 =A0end
> =A0end
>
> =A0module InstanceMethods
>
> =A0 =A0def read
> =A0 =A0 =A0@csv_contents =3D []
> =A0 =A0 =A0filename =3D self.class.to_s.downcase + '.txt'
> =A0 =A0 =A0file =3D File.new(filename)
> =A0 =A0 =A0@headers =3D file.gets.chomp.split(', ')
>
> =A0 =A0 =A0file.each do |row|
> =A0 =A0 =A0 =A0@csv_contents << row.chomp.split(', ')
> =A0 =A0 =A0end
> =A0 =A0end
>
> =A0 =A0attr_accessor :headers, :csv_contents
>
> =A0 =A0def initialize
> =A0 =A0 =A0read
> =A0 =A0end
>
> =A0end
>
> end

Note that the approach above has the disadvantage to read the whole
file into memory and store it there.  This could use up a lot memory
if the file is large - or even fail.  Often a better approach is to
use Ruby's block mechanism to yield one row at a time.  Then you can
still use that to create an Array of all rows.  Silly example (a row
is just a number here):

irb(main):019:0> def rows
irb(main):020:1> if block_given?
irb(main):021:2>   yield 1
irb(main):022:2>   yield 2
irb(main):023:2>   yield 3
irb(main):024:2> else
irb(main):025:2*   a =3D []
irb(main):026:2>   rows {|r| a << r}
irb(main):027:2>   a
irb(main):028:2> end
irb(main):029:1> end
=3D> nil
irb(main):030:0> rows {|r| p r}
1
2
3
=3D> 3
irb(main):031:0> rows
=3D> [1, 2, 3]

If there is no block rows invokes itself providing a block which
appends to the Array.

> class RubyCsv =A0# no inheritance! You can mix it in
> =A0include ActsAsCsv
> =A0acts_as_csv
> end
>
> m =3D RubyCsv.new
> puts m.headers.inspect
> puts m.csv_contents.inspect
>
> The task is to create an *each* method and return a CsvRow object
> instead of an array.
>
> So I figure I need to create the object first in the file.each loop
>
> This mess is where I got to:
>
> file.each do |row|
> =A0 =A0 =A0 =A0 / headers.each_with_index do |head, index|
> =A0 =A0 =A0 =A0 =A0require 'ostruct'
> =A0 =A0 =A0 =A0 =A0rowObject =3D OpenStruct.new
> =A0 =A0 =A0 =A0 =A0rowObject.#{@headers[index]} =3D row.chomp.split(', ')

This will assigne the whole row as an Array to a single member of
OpenStruct.  I don't think this is what you want.  Rather, you want
multiple assignments.  You could do it like this:

# get all the columns
columns =3D row.chomp.split(', ')

# create the row object
row =3D OpenStruct.new

# now assign all values by combining
# headers with columns
@headers.zip columns do |hd, col|
  row.send("#{hd}=3D", col)
end

Another approach would first create a Hash and use OpenStruct's Hash
construction:

irb(main):004:0> h =3D {"foo" =3D> 1, "bar" =3D> 2}
=3D> {"foo"=3D>1, "bar"=3D>2}
irb(main):005:0> o =3D OpenStruct.new(h)
=3D> #<OpenStruct foo=3D1, bar=3D2>
irb(main):006:0> o.foo
=3D> 1
irb(main):007:0> o.bar
=3D> 2

For that you could do

# get all the columns
columns =3D row.chomp.split(', ')

# combine into nested arrays and create Hash
h =3D Hash[@headers.zip columns]

# now create the row object
row =3D OpenStruct.new h

> =A0 =A0 =A0 =A0end
> =A0 =A0 =A0 =A0@csv_contents << rowObject
> =A0 =A0 =A0end
>
> But as you can see it's a mess
>
> I've also read some other posts around the internet and on here and
> thought this might work but haven't tried it yet
>
> file.each_with_index do |row, rowIndex|
> =A0 =A0 / headers.each_with_index do |head, headIndex|
> =A0 =A0 =A0 =A0require 'ostruct'
> =A0 =A0 =A0 =A0rowObject =3D OpenStruct.new( :#{@header[headIndex =3D>
> row[rowIndex]}
>
> Or something...
>
> All help and guidance gratefully received.

Hopefully that helped.

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/