2012-07-27 16 views
17

Oldukça basit bir regexp'im var, ancak temiz hale getirmek için normal adlandırılmış ifadeler kullanmak istedim ve sonuçların üzerine yinelemek istedim.Eşleştirilen eşleşmeleri MatchData içinde Hash ile eşleştirin Hata

Test dize:

testing_string = "111x222b333" 

Benim regexp'in: Büyü gibi

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) 

Bu çalışma, ama heres sorun gelir: hayır

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

# ERROR ! 

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

yoktur. MatchData nesnesinde each yöntemi ve ben gerçekten maymun yama istemiyorum.

Bu sorunu nasıl düzeltebilirim?

Düşündüğüm kadar açık değildim: buradaki adlar ve karma benzeri yapıları tutmaktır.

+0

Lütfen, bir dahaki sefere daha açık olun. Aksi takdirde benim gibi cevaplar açıklamadan sonra düşürülecek :) – mmdemirbas

cevap

32

yapabilirsiniz:

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

sadece ad/değer çiftleri üzerinde yineleme isterseniz:

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

Alternatifler:

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 
+0

Boyutları kullanan sürümler, aynı adın birden çok kez kullanılması durumunda çalışmaz. 'Boyutlar [name]' kullanımı, Ruby 2.4'ün named_captures gibi son başarılı eşleşmeyi döndürür. named_captures taklit etmek için aşağıdakileri kullanıyorum: 'matchdata.names.map {| n | [n, matchdata [n]]} .to_h' – oxc

-1

isimlere tutmak isterseniz tam bir Hash gerekiyorsa,

new_dimensions = {} 
dimensions.names.each { |k| new_dimensions[k] = my_operation(dimensions[k]) } 
+0

Tam olarak ne istediğimi, isimleri tutmak için. Kodunuz işe yaramaz, çünkü MatchData için [] = yöntemi yoktur. – blid

+0

Bunu kodunuzdan kopyaladım çünkü hash ile ne yapmak istediğini bilmiyordum. Sonuçları yeni bir karmaşa koymak için kodumu değiştirdim. Ancak Phrogz tarafından önerildiği gibi, "dimension.names.zip (dimension.captures)" yapmak bile daha temizdir. –

1

ben biraz farklı karma yaratma bütün sorunu saldırmak istiyorum: regex güçlü olmasına karşın

irb(main):052:0> testing_string = "111x222b333" 
"111x222b333" 
irb(main):053:0> hash = Hash[%w[width height depth].zip(testing_string.scan(/\d+/))] 
{ 
    "width" => "111", 
    "height" => "222", 
    "depth" => "333" 
} 

, onların siren-çağrısı çok çekici olabilir ve biz bunları kullanmaya çalışırken hayata batacak Bir şeyi başarmanın daha basit ya da basit yolları vardır. Düşünecek bir şey. OP yorum başına


, taranan öğelerin sayısını takip etmek için:

hash = Hash[%w[width height depth].zip(scan_result = testing_string.scan(/\d+/))] 
=> {"width"=>"111", "height"=>"222", "depth"=>"333"} 
scan_result.size 
=> 3 

Ayrıca hash.size o dönecektir gibi olur anahtarlar içeren dizinin boyutu, vb

Tüm yakalamalarınızın benzersiz adları varsa
+0

+1 İyi bir öneri. – Phrogz

+0

Ben sadece basit bir adamım ve normal ifadeyi korumak benim eğlence fikrim değil. :-) –

+0

Bu sadece bu durumda değil, tüm programlama KISS de çok iyi bir öneri. Her seferinde basitlik kuralına uymaya çalışıyorum, ama bu durumda maalesef her seferinde kaç sayı aldığımı takip etmem gerekiyor. – blid

1

@Phrogz's answer doğrudur, ancak birden çok yakalamayı aynı adaya verebilirsiniz. Here's an example from the Regexp documentation.

Bu kod adı aynı olan yakalar destekler:

captures = Hash[ 
    dimensions.regexp.named_captures.map do |name, indexes| 
    [ 
     name, 
     indexes.map { |i| dimensions.captures[i - 1] } 
    ] 
    end 
] 

# Iterate over the captures 
captures.each do |name, values| 
    # name is a String 
    # values is an Array of Strings 
end 
3

Yani bugün yeni Ruby sürümü (2.4.0) includes many new features, aralarında feature #11999, nam MatchData#named_captureswas released.

kod değişikliği Yani
h = '12'.match(/(?<a>.)(?<b>.)(?<c>.)?/).named_captures 
#=> {"a"=>"1", "b"=>"2", "c"=>nil} 
h.class 
#=> Hash 

dimensions = regexp.match(testing_string) 

dimensions = regexp.match(testing_string).named_captures 

için Ve sadece beğendikleri için regex maç sonucu each yöntemi kullanabilirsiniz: Bu, artık bunu yapabilirsiniz demektir diğer Hash da.

İlgili konular