2016-05-12 157 views
0

我在寫一個過濾器程序,它讀取包含地址數據的CSV文件,並排除位於新月(cres),大道(ave)或地點(pl)中的行。Chaning Ruby正則表達式運算符

下面是一些例子輸入:

data = <<CSV 
ID,Street address,Town,Valuation date,Value 
1,1 Northburn RD,WANAKA,1/1/2015,280000 
2,1 Mount Ida PL,WANAKA,1/1/2015,280000 
3,1 Mount Linton AVE,WANAKA,1/1/2015,780000 
4,1 Centre CRES,WANAKA,1/1/2015,295000 
CSV 

require 'csv' 

elements = [] 
CSV.parse(data, headers: true, header_converters: :symbol) do |row| 
    elements << row.to_h 
end 
elements 
#=> [ 
#  {:id=>"1", :street_address=>"1 Northburn RD", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"280000"}, 
#  {:id=>"2", :street_address=>"1 Mount Ida PL", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"280000"}, 
#  {:id=>"3", :street_address=>"1 Mount Linton AVE", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"780000"}, 
#  {:id=>"4", :street_address=>"1 Centre CRES", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"295000"} 
# ] 

我可以用簡單的正則表達式來過濾三個中的一個,即/pl//cres//ave/,但他們使用&&我不能鏈條:(也不它們的功能,當我把它們分成三個獨立的「過濾器」)

elements.select { |e| e[:street_address].downcase! !~ /pl/ && e[:street_address].downcase! !~ /cres/ && e[:street_address].downcase! !~ /ave/ } 
#=> [ 
#  {:id=>"1", :street_address=>"1 northburn rd", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"280000"}, 
#  {:id=>"3", :street_address=>"1 mount linton ave", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"780000"}, 
#  {:id=>"4", :street_address=>"1 centre cres", :town=>"WANAKA", :valuation_date=>"1/1/2015", :value=>"295000"} 
# ] 

這種過濾掉條目#2如預期,但沒有#3,#4。

任何想法我失蹤?

+1

是讀取CSV文件有關您的問題的代碼? (你應該使用Ruby的[CSV](http://ruby-doc.org/stdlib-2.3.1/libdoc/csv/rdoc/CSV.html)庫) – Stefan

+0

不,它不是,工作得很好。只是過濾器部分是狡猾的,但我想我會包括一切,以防萬一:) 我也看看CSV庫! –

+0

發佈其他人可以複製粘貼的代碼通常會更好。你能否爲'elements'和期望的輸出添加一些示例數據? – Stefan

回答

5

這是因爲downcase! - 它改變了接收器和返回nil,如果未進行任何更改。

str = 'FOO' 
str.downcase! #=> "foo" 
str.downcase! #=> nil 

因此,你的第二個比較變得nil !~ /cres/這始終是true

要解決你的代碼,使用downcase(不!):

elements[:streetAddress].downcase !~ /pl/ 

或添加i到你的正則表達式,使其不區分大小寫:

elements[:streetAddress] !~ /pl/i 

此外,還可以結合您的正則表達式和使用方法reject

elements.reject { |e| e[:streetAddress] =~ /pl|cres|ave/i } 

要只匹配字符串年底與「PL」,「CRES」,或「AVE」,使用適當的anchor,例如/(pl|cres|ave)$/i

+0

...是一個體面的點...所以我會做三個單獨的upcase/downcase轉換?這對我來說似乎有些落後。 –

+0

@TinusWagner當然不是,我用另一種方式更新了我的答案。 – Stefan

+0

你先生是個傳奇人物。謝謝。 –

0

如果要刪除基於一個條件的數組元素,慣用的方式可能是使用Array#delete_if

IMO,儘量不要使用正則表達式時,你已經知道哪些值被接受。正則表達式在模式匹配方面非常出色(檢查電子郵件的有效性等),但它們的使用不應該走得更遠。

假設RD,CRES,AVE總是在最後一個字,這個工程:

x = elements.delete_if do |el| 
    ['pl', 'cres', 'ave'].include?(el[:streetAddress].downcase.split.last) 
end 
+0

嘿floum,如果我們簡單地使用'pl'作爲移除條件的實例,那麼它將不會工作,如果streetAddress的完整字符串是「55 Mt Gold AVE」,例如對嗎? –

+0

我在回答後看到您剛剛從CSV提交了數據。我必須檢查一下,並在幾個回覆。 – floum

+1

感謝您的努力@floum –