有没有一种优雅的方法来纠正加拿大邮政编码中的常见数据输入错误?

时间:2011-08-23 20:09:05

标签: ruby-on-rails ruby

我正在处理的应用程序从许多外部来源获取加拿大地址并映射它们。一个非常罕见的映射错误来源是邮政编码中的数据输入错误,即输入大写o而不是0,大写i而不是1,依此类推。

出于此问题的目的,假设所有邮政编码数据都已删除所有空格,已大写,并且如果长度超过则截断为6个字符。在这种情况下,有效的加拿大邮政编码将采用“A1A1A1”格式。什么是优雅的方式来进行上述替换?

我们有一个有效的解决方案,但这已经成为我们办公室里一个有趣的问题,只是为了看看人们想出了什么。到目前为止,没有任何东西能够“令人敬畏”。

其他一些条件:

  • 如果邮政编码已经无效(例如只有4个字符)可以随意更新或忽略它
  • 替换应该是双向的,即无效字母应转换为数字,无效数字应转换为字母。
  • 必须在ruby中完成,因为这是一个rails应用程序

3 个答案:

答案 0 :(得分:3)

def clean_postal_code
  swaps = {'L' => '1', 'O' => '0'} #fill with whatever necessary substitutions
  pc = self.postal_code
  (pc.size/2).times do |n|
    swaps.each do |letter, number|
      pc[n*2+1] = pc[n*2+1].chr.gsub(letter, number) #only want numbers on odd indices
      pc[n*2] = pc[n*2].chr.gsub(number, letter) #only characters on even indices
    end
  end
  self.postal_code = pc
end

答案 1 :(得分:3)

与Tron相似,但(恕我直言)更简单,并且不需要数字到字母的地图是对称的:

chars = { '0' => 'O', '1' => 'I'             } # etc.
nums  = { 'O' => '0', 'I' => '1', 'Q' => '0' } # etc.
maps  = [
        ->(c) { chars[c.upcase] || c.upcase }, 
        ->(c) { nums[ c.upcase] || c }
]
clean = (0 ... input.length).map { |i| maps[i % 2].call(s[i]) }.join

您从脏input开始,并以clean作为修正版本。例如,这会将'aib100'转换为'A1B1O0'

如果需要,你可以将它包装在一个函数中:

def unmangle(input)
    chars = { '0' => 'O', '1' => 'I'             } # etc.
    nums  = { 'O' => '0', 'I' => '1', 'Q' => '0' } # etc.
    maps  = [
            ->(c) { chars[c.upcase] || c.upcase }, 
            ->(c) { nums[ c.upcase] || c }
    ]
    (0 ... input.length).map { |i| maps[i % 2].call(input[i]) }.join
end

clean = unmangle('aib100')
# 'A1B1O0'

您也可以使用此(或其他各种类似的结构):

(0 ... input.length).inject(input) { |s, i| s[i] = maps[i % 2].call(s[i]); s }

如果您不喜欢map / join版本。

答案 2 :(得分:1)

我认为这有点像一样紧凑,你可以使代码挑战。我不一定真的这样写 - 像Tron的回答可能更容易维护。

那就是说,这样的事情原则上应该起作用:

def clean_pc(pc)
  pc.split(/(?<=\G..)/).map {|a| a[0].tr('01','OL') + a[1].tr('OoLliI','001111') }.join
end

(UPDATE)

...或者,更容易阅读,并且可能足以维持生产使用:

def clean_pc(pc)
  pc.chars.each_slice(2).map {|a| a[0].tr('01','OL') + a[1].tr('OoLliI','001111')}.join
end