Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert named matches in MatchData to Hash

Tags:

I have a rather simple regexp, but I wanted to use named regular expressions to make it cleaner and then iterate over results.

Testing string:

testing_string = "111x222b333"

My regexp:

regexp = %r{
                (?<width> [0-9]{3} ) {0}
                (?<height> [0-9]{3} ) {0}
                (?<depth> [0-9]+ ) {0}

                \g<width>x\g<height>b\g<depth>
            }x
dimensions = regexp.match(testing_string)

This work like a charm, but heres where the problem comes:

dimensions.each { |k, v| dimensions[k] = my_operation(v) }

# ERROR !

 undefined method `each' for #<MatchData "111x222b333" width:"111" height:"222" depth:"333">.

There is no .each method in MatchData object, and I really don't want to monkey patch it.

How can I fix this problem ?

I wasn't as clear as I thought: the point is to keep names and hash-like structure.

like image 358
blid Avatar asked Jul 27 '12 13:07

blid


2 Answers

If you need a full Hash:

captures = Hash[ dimensions.names.zip( dimensions.captures ) ]
p captures
#=> {"width"=>"111", "height"=>"222", "depth"=>"333"}

If you just want to iterate over the name/value pairs:

dimensions.names.each do |name|
  value = dimensions[name]
  puts "%6s -> %s" % [ name, value ]
end
#=>  width -> 111
#=> height -> 222
#=>  depth -> 333

Alternatives:

dimensions.names.zip( dimensions.captures ).each do |name,value|
  # ...
end

[ dimensions.names, dimensions.captures ].transpose.each do |name,value|
  # ...
end

dimensions.names.each.with_index do |name,i|
  value = dimensions.captures[i]
  # ...
end
like image 164
Phrogz Avatar answered Sep 29 '22 16:09

Phrogz


So today a new Ruby version (2.4.0) was released which includes many new features, amongst them feature #11999, aka MatchData#named_captures. This means you can now do this:

h = '12'.match(/(?<a>.)(?<b>.)(?<c>.)?/).named_captures
#=> {"a"=>"1", "b"=>"2", "c"=>nil}
h.class
#=> Hash

So in your code change

dimensions = regexp.match(testing_string)

to

dimensions = regexp.match(testing_string).named_captures

And you can use the each method on your regex match result just like on any other Hash, too.

like image 42
amoebe Avatar answered Sep 29 '22 16:09

amoebe