Что-то вроде:
1: преобразовать имена в массивы:
irb> names.map!{|n|n.scan(/[^\s.]+\.?/)}
["J.", "R.", "Smith"]
["John", "R.", "Smith"]
["John", "Smith"]
["John", "Roy", "Smith"]
["Johnny", "Smith"]
2: некоторая функция идентичности:
for a,b in names.combination(2)
p [(a&b).size,a,b]
end
[2, ["J.", "R.", "Smith"], ["John", "R.", "Smith"]]
[1, ["J.", "R.", "Smith"], ["John", "Smith"]]
[1, ["J.", "R.", "Smith"], ["John", "Roy", "Smith"]]
[1, ["J.", "R.", "Smith"], ["Johnny", "Smith"]]
[2, ["John", "R.", "Smith"], ["John", "Smith"]]
[2, ["John", "R.", "Smith"], ["John", "Roy", "Smith"]]
[1, ["John", "R.", "Smith"], ["Johnny", "Smith"]]
[2, ["John", "Smith"], ["John", "Roy", "Smith"]]
[1, ["John", "Smith"], ["Johnny", "Smith"]]
[1, ["John", "Roy", "Smith"], ["Johnny", "Smith"]]
Или вместо &
вы можете использовать .permutation
+ .zip
+ .max
, чтобы применить некоторую пользовательскую функцию, которая определяет, идентичны ли части имен.
UPD:
aim = 'Rob Bobbie Johnson'
candidates = [
"Bob Robbie John",
"Bobbie J. Roberto",
"R.J.B.",
]
$synonyms = Hash[ [
["bob",["bobbie"]],
["rob",["robbie","roberto"]],
] ]
def prepare name
name.scan(/[^\s.]+\.?/).map &:downcase
end
def mf a,b # magick function
a.zip(b).map do |i,j|
next 1 if i == j
next 0.9 if $synonyms[i].to_a.include?(j) || $synonyms[j].to_a.include?(i)
next 0.5 if i[/\.$/] && j.start_with?(i.chomp '.')
next 0.5 if j[/\.$/] && i.start_with?(j.chomp '.')
-10 # if some part of name appears to be different -
# it's bad even if another two parts were good
end.inject :+
end
for c in candidates
results = prepare(c).permutation.map do |per|
[mf(prepare(aim),per),per]
end
p [results.transpose.first.max,c]
end
[-8.2, "Bob Robbie John"] # 0.9 + 0.9 - 10 # Johnson != John # I think ..)
[2.4, "Bobbie J. Roberto"] # 1 + 0.9 + 0.5 # Rob == Roberto, Bobbie == Bobbie, Johnson ~~ J.
[1.5, "R.J.B."] # 0.5 + 0.5 + 0.5