Can using the ruby flip-flop as a filter be made less kludgy?

448 views Asked by At

In order to get part of text, I'm using a true if kludge in front of a flip-flop:

desired_portion_lines = text.each_line.find_all do |line|
  true if line =~ /start_regex/ .. line =~ /finish_regex/
end
desired_portion = desired_portion_lines.join

If I remove the true if bit, it complains

bad value for range (ArgumentError)

Is it possible to make it less kludgy, or should I merely do

desired_portion_lines = ""
text.each_line do |line|
  desired_portion_lines << line if line =~ /start_regex/ .. line =~ /finish_regex/
end

Or is there a better approach that doesn't use enumeration?

3

There are 3 answers

2
the Tin Man On

I think

desired_portion_lines = ""
text.each_line do |line|
  desired_portion_lines << line if line =~ /start_regex/ .. line =~ /finish_regex/
end

is perfectly acceptable. The .. operator is very powerful, but not used by a lot of people, probably because they don't understand what it does. Possibly it looks weird or awkward to you because you're not used to using it, but it'll grow on you. It's very common in Perl when dealing with ranges of lines in text files, which is where I first encountered it, and eventually was using it a lot.

The only thing I'd do differently is add some parenthesis to visually separate the logical tests from each other, and from the rest of the line:

desired_portion_lines = ""
text.each_line do |line|
  desired_portion_lines << line if ( (line =~ /start_regex/) .. (line =~ /finish_regex/) )
end

Ruby (and Perl) coders seem to abhor using parenthesis, but I consider them useful for visually separating the logic tests. For me it's a readability and, by extension, a maintenance thing.

The only other thing I can think of that might help, would be to change desired_portion_lines to an array, and push your selected lines onto it. Currently, using desired_portion_lines << line appends to the string, mutating it each time. It might be faster pushing on the array then joining its elements afterward to build your string.

Back to the first example. I didn't test this but I think you can simplify it to:

desired_portion = text.each_line.find_all { |line| line =~ /start_regex/ .. line =~ /finish_regex/ }.join

The only downside to iterating over all lines in a file using the flip-flop, is that if the start-pattern can occur multiple times, you'll get each found block added to desired_portion.

0
kurumi On

if you are doing it line by line, my preference is something like this

line =~ /finish_regex/ && p=0
line =~ /start_regex/ && p=1
puts line if p

if you have all in one string. I would use split

mystring.split(/finish_regex/).each do |item|
  if item[/start_regex/] 
     puts item.split(/start_regex/)[-1]
  end
end
0
Petr Skocik On

You can save three characters by replacing true if with !!() (with the flip flop belonging in between the parentheses).