3
\$\begingroup\$

I was given a file of size 46KB full of names (around ~5000), in the format as shown below:

"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH"..."DARRELL","ALONSO"

I wonder if my code below is optimal in doing what I want, which is to translate this txt file into an array of names.

array = []
File.readlines('names.txt').each do |l|
  array << l.delete("\"").split(",")
end

array.flatten
\$\endgroup\$
3
\$\begingroup\$

Yes it is, it takes 0.372798 seconds to run on my computer for a 50 MB file, for a 46KB file this runs in the blink of an eye.

Anyway as this is Codereview I suggest an equally fast but higher level version of your code:

def get_names(filename)
    File.read(filename).tr('"', '').split(',')
end
\$\endgroup\$
  • \$\begingroup\$ Thanks caridorc. Curious how you generate the 50MB file? :P \$\endgroup\$ – Chris Yeung Apr 7 '15 at 11:27
  • \$\begingroup\$ and yea, i think File.read is what I am looking for instead of using File.readlines, because there is just one line \$\endgroup\$ – Chris Yeung Apr 7 '15 at 11:29
  • 2
    \$\begingroup\$ @ChrisYeung Obvious, by hand, I had 8 hours to spare :P (not really, the code is def rand_string(len) (0...len).map { (65 + rand(26)).chr }.join end def gen_file names = (1..10000).map{|x| '"'+rand_string(x)+'"'}.join(',') File.write("names.txt", names) end \$\endgroup\$ – Caridorc Apr 7 '15 at 11:50
  • \$\begingroup\$ @200_success made a valid point where there may be names with commas within the quotes. how should we address that? \$\endgroup\$ – Chris Yeung Apr 9 '15 at 13:45
2
\$\begingroup\$

You can also do

def get_names(filename)
  File.read(filename).scan(/[a-z\s]+/i)
end

In this case, the scan looks for contiguous runs of letters and spaces, and captures those. So instead of removing quotes and splitting on commas, we're capturing stuff that isn't a comma or quote.

Don't know if it's faster, though (haven't tried it). Just presenting an alternative.

\$\endgroup\$
2
\$\begingroup\$

My first concern is correctness, not speed. The input string is quoted for a reason, presumably to allow for the possibility of a literal comma within one of the names, e.g. "MARY, QUEEN OF SCOTS". Granted, it would be weird for that situation to actually happen, but I think it's poor practice to disregard the quotes completely.

Therefore, I would go with @Snowbody's approach. I would refine it a bit by stripping the double-quotes from just the first and last items of each line, based on the reasoning that modifying short strings should be faster than copying the entire line.

File.readlines('names.txt').flat_map do |line|
  line_names = line.split('","')
  line_names[0].delete!('"')
  line_names[-1].chomp!(',')
  line_names[-1].chomp!('"')
  line_names
end
\$\endgroup\$
  • \$\begingroup\$ That's definitely better, good job. Do string operations on the short string not the long one. \$\endgroup\$ – Snowbody Apr 9 '15 at 13:50
1
\$\begingroup\$

This may or may not be faster:

def get_names(filename)
    ('",' + File.read(filename) + ',"').split('","')
end

due to the initial insert possibly reallocating.

\$\endgroup\$
  • \$\begingroup\$ It seems a bit hacky but i got your point. \$\endgroup\$ – Chris Yeung Apr 9 '15 at 13:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.