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.
source to share
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.
source to share
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.
source to share
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
source to share