Gordon Fontenot
Gordon Fontenot

Reputation: 1380

Is there a way to pass a regex capture to a block in Ruby?

I have a hash with a regex for the key and a block for the value. Something like the following:

{ 'test (.+?)' => { puts $1 } }

Not exactly like that, obviously, since the block is being stored as a Proc, but that's the idea.

I then have a regex match later on that looks a lot like this

hash.each do |pattern, action|
    if /#{pattern}/i.match(string)
        action.call
    end
end

The idea was to store the block away in the hash to make it a bit easier for me to expand upon in the future, but now the regex capture doesn't get passed to the block. Is there a way to do this cleanly that would support any number of captures I put in the regex (as in, some regex patterns may have 1 capture, others may have 3)?

Upvotes: 1

Views: 319

Answers (2)

cam
cam

Reputation: 14222

What if you pass the match data into your procs?

hash.each do |pattern, action|
  if pattern.match(string)
    action.call($~)
  end
end

Your hash would become:

{ /test (.+?)/i => lambda { |matchdata| puts matchdata[1] } }

Upvotes: 3

the Tin Man
the Tin Man

Reputation: 160551

I would use Hash.find which walks the hash elements, passing them into a block, one at a time. The one that returns true wins:

Something like this:

hash = {/foo/ => lambda { 'foo' }, /bar/ => lambda { 'bar' } }
str = 'foo'
puts hash.find{ |n,v| str =~ n }.to_a.last.call

Obviously I'm using lambda but it's close enough. And, if there was no match you need to handle nil values. For the example I chained to_a.last.call but in real life you'd want to react to a nil otherwise Ruby will get mad.

If you are searching through a lot of patterns, or processing a lot of text, your search will be slowed down by having to recompile the regex each time. I'd recommend storing the keys as regex objects to avoid that.

Upvotes: 1

Related Questions