Would it be better to use collection in this situation?

I'm just getting started with Ruby and I wrote some code to do some basic parsing of a CSV file (Line is the base class, omitted for brevity):

class File

  def each_csv
    each do |line|
      yield line.split(",")
    end
  end

end

lines = Array.new

File.open("some.csv") do |file|
  file.each_csv do |csv| 
    lines << Line.new(:field1 => csv[0], :field2 => csv[1])
  end
end

      

I have a feeling that I'm better off using collect

rather than pushing each one Line

onto an array, but I can't figure out how to do this.

Can anyone show me how to do this or is it fine as it is?

Edit: I should have made it clear that I don't actually use this code in production, it's more used to the constructs of the language. It's still good to know that there are libraries to do this right, though.

+1


source to share


4 answers


Here's a (possibly wild) idea, use the Struct class instead of rolling your own simple POD class. But from this you should have a constructor that takes all the arguments that can be generated from the file data.

Line = Struct.new(:field1, :field2, :field3)

      

Then at the heart of the algorithm you want something like:

File.open("test.csv").lines.inject([]) do |result, line|
    result << Line.new(line.split(",", Line.length))
end

      

or a little more concise and functional:



lines = File.open("test.csv").lines.map { |line| Line.new(line.split(",", Line.length)) }

      

To be honest, I haven't used the Struct class very often, but I should be, and I'll probably use the refactoring I've already written to use it. It allows you to access variables by their names, for example:

Line.field1 = blah
Line.field2 = 1

      

Ruby Struct class .

So, to answer your question , and looking at the code above, I would say that it is much easier to use the collect / map command to perform the computation. The map function along with the injection is very powerful and I find I use them quite often.

+4


source


I don't know if you know about this, but ruby ​​has its own class for parsing and writing CSV files.

I found an example using collect to turn a CSV file into an array of hashes.

def csv_to_array(file_location)
  csv = CSV::parse(File.open(file_location, 'r') {|f| f.read })
  fields = csv.shift
  csv.collect { |record| Hash[*(0..(fields.length - 1)).collect {|index| [fields[index],record[index].to_s] }.flatten ] }
end

      



This example is taken from this article .

If you are unfamiliar with the concept of *, it basically dissolves the outer [] parentheses, turning the array into a comma-separated list of elements.

+2


source


You looked at FasterCSV, it does what you are trying to do here and also deals with some brain dead center that you find in some CSV files.

+1


source


See how it works for you (functional programming is fun!):

Try using injection. Inject takes a starting "accumulator" as a parameter, and then a block of two parameters:

[1,2,3].inject(0) { |sum,num| sum+num }

      

naturally 6

[1,2,3].inject(5) { |sum,num| sum+num }

      

is 11

[1,2,3].inject(2) { |sum,num| sum*num }

      

is 12

To the moment:

class Line
  def initialize(options)
    @options = options
  end

  def to_s
    @options[:field1]+" "+@options[:field2]
  end
end

File.open("test.csv").lines.inject([]) do |lines,line|
  split = line.split(",")
  lines << Line.new(:field1 => split[0],:field2 => split[1])
end

      

0


source







All Articles