class OfacMatch
attr_reader :possible_hits
#Intialize a Match object with a record hash of fields you want to match on.
#Each key in the hash, also has a data hash value for the weight, token, and type.
#
# match = Ofac::Match.new({:name => {:weight => 10, :token => 'Kevin Tyll'},
# :city => {:weight => 40, :token => 'Clearwater', },
# :address => {:weight => 40, :token => '1234 Park St.', },
# :zip => {:weight => 10, :token => '33759', :type => :number}})
#
# data hash keys:
# * data[:weight] - value to apply to the score if there is a match (Default is 100/number of key in the record hash)
# * data[:token] - string to match
# * data[:match] - set from records hash
# * data[:score] - output field
# * data[:type] - the type of match that should be performed (valid values are +:sound+ | +:number+) (Default is +:sound+)
def initialize(stats={})
@possible_hits = []
@stats = stats.dup
weight = 100
weight = 100 / @stats.length if @stats.length > 0
@stats.each_value do |data|
data[:weight] ||= weight
data[:match] ||= ''
data[:type] ||= :sound
data[:score] ||= 0
data[:token] = data[:token].to_s.upcase
end
end
# match_records is an array of hashes.
#
# The hash keys must match the record hash keys set when initialized.
#
# score will return the highest score of all the records that
# are sent in match_records.
def score(match_records)
score_results = Array.new
unless match_records.empty?
#place the match_records information
#into our @stats hash
match_records.each do |match|
match.each do |key, value|
@stats[key.to_sym][:match] = value.to_s.upcase
end
record_score = calculate_record
score_results.push(record_score)
@possible_hits << match.merge(:score => record_score) if record_score > 0
end
score = score_results.max #take max score
end
@possible_hits.uniq!
score ||= 0
end
private
# calculate the score for this record
# comparing the token to the match fields in the @stats hash
# and storing the score into the record
def calculate_record
score = 0
unless @stats.nil?
#need to make sure we check the name first, since city and address don't
#get added to the score unless there is a name match
[:name,:city,:address].each do |field|
data = @stats[field]
if (data[:token].blank?)
value = 0 #token is blank can't be sure of a match if nothing to match against
else
if (data[:match].blank?)
value = 0 #token has value match is blank
else
#token and match both have values
if (data[:type] == :number)
value = data[:token] == data[:match] ? 1 : 0
else
#first see if there is an exact match
value = data[:token] == data[:match] ? 1 : 0
unless value > 0
#do a sounds like with the data as given to see if we get a match
#if match on sounds_like, only give .75 of the weight.
value = data[:token].ofac_sounds_like(data[:match],false) ? 0.75 : 0
end
#if no match, then break the data down and see if we can find matches on the
#individual words
unless value > 0
token_data = data[:token].gsub(/\W/,'|')
token_array = token_data.split('|')
token_array.delete('')
match_data = data[:match].gsub(/\W/,'|')
match_array = match_data.split('|')
match_array.delete('')
value = 0
partial_weight = 1/token_array.length.to_f
token_array.each do |partial_token|
#first see if we get an exact match of the partial
if match_array.include?(partial_token)
value += partial_weight
else
#otherwise, see if the partial sounds like any part of the OFAC record
match_array.each do |partial_match|
if partial_match.ofac_sounds_like(partial_token,false)
#give partial value for every part of token that is matched.
value += partial_weight * 0.75
break
end
end
end
end
end
end
end
end
data[:score] = data[:weight] * value
score += data[:score]
break if field == :name && data[:score] == 0
end
end
score.round
end
end