Skip to content

Commit 7af6a1a

Browse files
committed
Fix String#similarity to divide by the longer length
similarity divided the shared length by (longest length + 1), so 9 of 10 matching characters scored 0.818 instead of the 0.9 its docs showed, and non-identical strings could never approach 1.0. It now divides by the longest length. The +1 did stop one thing: comparing an empty String with a zero-length non-String (an empty Symbol or Array) gets past the equality check, and 0.0/0 is NaN. That case now returns 0.0 explicitly. Restores 0.9 in the docs and demo, updates the unit test's expected value (0.75 -> 0.818), and adds tests for the 9-of-10 case and the empty non-String arguments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2Njj4j1r9twkVcXsMySLW
1 parent 3d79004 commit 7af6a1a

4 files changed

Lines changed: 18 additions & 5 deletions

File tree

‎HISTORY.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ Changes:
6262
quietly changed both.
6363
* `Enumerable::Argumentable#min` and `#max` now pass their arguments to
6464
`#each`, as documented, rather than to Ruby's own `min(n)`/`max(n)`.
65+
* `String#similarity` now divides the shared length by the longer string's
66+
length, not length + 1, so 9 of 10 matching characters score 0.9 as its
67+
docs said (it gave 0.818). Scores for non-identical strings rise slightly.
6568
* `require 'facets'` no longer crashes on Ruby 4.1, which removed
6669
`ObjectSpace._id2ref`. `ObjectSpace[]` is only defined where `_id2ref`
6770
exists.

‎demo/core/string/similarity.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
A fuzzy matching mechanism. Returns a score from 0-1, based on the number of
66
shared edges. To be effective, the strings must be of length 2 or greater.
77

8-
"Alexsander".similarity("Aleksander").round(2).assert == 0.82
8+
"Alexsander".similarity("Aleksander").assert == 0.9
99

1010
"Alexander".similarity("Alexander").assert == 1.0
1111

‎lib/core/facets/string/similarity.rb‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class String
44
# on the number of shared edges. To be effective, the strings
55
# must be of length 2 or greater.
66
#
7-
# "Alexsander".similarity("Aleksander") #=> 0.8181818181818182
7+
# "Alexsander".similarity("Aleksander") #=> 0.9
88
#
99
# The way it works:
1010
#
@@ -82,8 +82,9 @@ def similarity(str_in)
8282

8383
score = 0.0
8484
matches.each{ |mm| score += mm.length }
85-
self.length > str_in.length ? largest = self.length : largest = str_in.length
86-
return score/(largest+1)
85+
largest = [self.length, str_in.length].max
86+
return 0.0 if largest.zero? # e.g. an empty Symbol or Array
87+
return score/largest
8788
end
8889

8990
end

‎test/core/string/test_similarity.rb‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
method :similarity do
66

77
test do
8-
"Alexsanders".similarity("Aleksander").assert == 0.75
8+
"Alexsanders".similarity("Aleksander").round(3).assert == 0.818
99
end
1010

1111
test do
@@ -16,6 +16,15 @@
1616
"Alexander".similarity("").assert == 0.0
1717
end
1818

19+
test "9 of 10 characters shared" do
20+
"Alexsander".similarity("Aleksander").assert == 0.9
21+
end
22+
23+
test "empty non-String argument" do
24+
"".similarity(:"").assert == 0.0
25+
"".similarity([]).assert == 0.0
26+
end
27+
1928
end
2029

2130
end

0 commit comments

Comments
 (0)