2016-05-12 115 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 –