Techioz Blog

MatchData の名前付き一致をハッシュに変換する

概要

かなり単純な正規表現がありますが、名前付き正規表現を使用してそれをクリーンにし、結果を反復処理したいと考えました。

テスト文字列:

testing_string = "111x222b333"

私の正規表現:

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)

これは魅力的に機能しますが、ここで問題が発生します。

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

# ERROR !

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

MatchData オブジェクトには .each メソッドがないので、モンキーパッチを適用したくありません。

この問題はどうすれば解決できますか?

思ったほど明確ではありませんでした。重要なのは、名前とハッシュのような構造を維持することです。

解決策

完全なハッシュが必要な場合:

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

名前と値のペアを反復処理するだけの場合は、次のようにします。

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

代替案:

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