HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyModerate

Counting words / lines in Ruby

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
wordscountinglinesruby

Problem

I solved this problem in Ruby:


Write an utility that takes 3 command-line parameters P1, P2 and P3.
P3 is OPTIONAL (see below) P1 is always a file path/name. P2 can take
the values:



  • “lines”



  • “words”



  • “find”





Only P2 is “find”, then P3 is relevant/needed, otherwise it is not.


So, the utility does the following:



  • If P2 is “rows” it says how many lines it has



  • If P2 is “words” it says how many words it has (the complete file)



  • If P2 is “find” it prints out the lines where P3 is present




My solution looks like this:

```
#!/usr/bin/env ruby

def print_usage
puts "Usage: #{$0} words|lines"
puts " #{$0} find "
end

class LineCounter
# Initialize instance variables
def initialize
@line_count = 0
end
def process(line)
@line_count += 1
end
def print_result
puts "#{@line_count} lines"
end
end

class WordCounter
# Initialize instance variables
def initialize
@word_count = 0
end
def process(line)
@word_count += line.scan(/\w+/).size
end
def print_result
puts "#{@word_count} words"
end
end

class WordMatcher
# Initialize instance variables, using constructor parameter
def initialize(word_to_find)
@matches = []
@word_to_find = word_to_find
end
def process(line)
if line.scan(/#{@word_to_find}/).size > 0
@matches << line
end
end
def print_result
@matches.each { |line|
puts line
}
end
end

# Main program
if __FILE__ == $PROGRAM_NAME

processor = nil

# Try to find a line-processor
if ARGV.length == 2
if ARGV[1] == "lines"
processor = LineCounter.new
elsif ARGV[1] == "words"
processor = WordCounter.new
end
elsif ARGV.length == 3 && ARGV[1] == "find"
word_to_find = ARGV[2]
processor = WordMatcher.new(word_to_find)
end

if not processor
# Print usage and exit if no processor found
print_usage
exit 1
else
# Process the lines and print result

Solution

Some notes:

  • Those counter classes are probably overkill, keep it simple.



  • Ruby is an OOP language, but it's not necessary to create a bunch of classes for simple scripts like this.



  • Idiomatic: if not x -> if !x



  • Idiomatic: { ... } for one-line blocks, do/end for multi-line.



I'd write:

fail("Usage: #{0} PATH (lines|words|find REGEXP)") unless ARGV.size >= 2
path, mode, optional_regexp = ARGV

open(path) do |fd|
  case mode
  when "lines"
    puts(fd.lines.count)
  when "words"
    puts(fd.lines.map { |line| line.split.size }.reduce(0, :+))
  when "find"
    if optional_regexp
      fd.lines.each { |line| puts(line) if line.match(optional_regexp) }
    else
      fail("mode find requires a REGEXP argument")
    end
  else
    fail("Unknown mode: #{mode}")
  end
end

Code Snippets

fail("Usage: #{0} PATH (lines|words|find REGEXP)") unless ARGV.size >= 2
path, mode, optional_regexp = ARGV

open(path) do |fd|
  case mode
  when "lines"
    puts(fd.lines.count)
  when "words"
    puts(fd.lines.map { |line| line.split.size }.reduce(0, :+))
  when "find"
    if optional_regexp
      fd.lines.each { |line| puts(line) if line.match(optional_regexp) }
    else
      fail("mode find requires a REGEXP argument")
    end
  else
    fail("Unknown mode: #{mode}")
  end
end

Context

StackExchange Code Review Q#41480, answer score: 15

Revisions (0)

No revisions yet.