将MatchData中的命名匹配转换为Hash

bli*_*lid 20 ruby regex hash

我有一个相当简单的正则表达式,但我想使用命名正则表达式使其更清晰,然后迭代结果.

测试字符串:

testing_string = "111x222b333"
Run Code Online (Sandbox Code Playgroud)

我的正则表达式:

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)
Run Code Online (Sandbox Code Playgroud)

这项工作就像一个魅力,但问题来自何处:

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

# ERROR !

 undefined method `each' for #<MatchData "111x222b333" width:"111" height:"222" depth:"333">.
Run Code Online (Sandbox Code Playgroud)

没有.each在MatchData对象中的方法,我真的不想修补它.

我该如何解决这个问题?

我不像我想的那么清楚:重点是保持名称和类似哈希的结构.

Phr*_*ogz 37

如果你需要一个完整的哈希:

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

如果您只想迭代名称/值对:

dimensions.names.each do |name|
  value = dimensions[name]
  puts "%6s -> %s" % [ name, value ]
end
#=>  width -> 111
#=> height -> 222
#=>  depth -> 333
Run Code Online (Sandbox Code Playgroud)

备择方案:

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
Run Code Online (Sandbox Code Playgroud)


amo*_*ebe 5

因此,今天发布了一个新的Ruby版本(2.4.0)其中包括许多新功能,其中包括#11999,又名MatchData#named_captures。这意味着您现在可以执行以下操作:

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

因此,在您的代码更改中

dimensions = regexp.match(testing_string)
Run Code Online (Sandbox Code Playgroud)

dimensions = regexp.match(testing_string).named_captures
Run Code Online (Sandbox Code Playgroud)

您也可以each像在其他正则表达式匹配结果上一样使用该方法Hash