Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- Release publishing now validates the tag/version and runs formatting, lint, and tests before publishing the verified build.

### Fixed
- Calculate `similarityScore` lengths and edit distance after lowercasing so Unicode case expansion cannot produce negative scores or incorrect similarity ratios.
- Prevent invalid levels from hanging `tashfeer`, `tashfeerBannedWords`, and `toOldArabicAndTashfeerBannedWords`: non-number values throw `TypeError`; negative, fractional, and non-finite numbers throw `RangeError`. Random-index generation handles empty input and caps the requested count at the word length ([#35](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/issues/35), [PR #36](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/pull/36)).
- Attempt browser bundle uploads after a successful build even if npm publishing fails ([commit 598035a](https://github.com/Seen-Arabic/Arabic-Services-JavaScript/commit/598035a)).

Expand Down
9 changes: 5 additions & 4 deletions src/utils/string.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ export function setCharAt(str: string, index: number, chr: string): string {
}

/**
* Calculates a similarity score between two strings, potentially based on their edit distance.
* Calculates a case-insensitive similarity score using Levenshtein distance over UTF-16 code units.
* Both the distance and string lengths are measured after lowercasing.
*
* @param {string} s1 - The first string to compare.
* @param {string} s2 - The second string to compare.
* @returns {number} A similarity score representing how similar the two strings are. The score is between 0 and 1.
*/
export function similarityScore(s1: string, s2: string): number {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();

let longer = s1;
let shorter = s2;
// swap them if s1 is bigger than s2
Expand All @@ -45,9 +49,6 @@ export function similarityScore(s1: string, s2: string): number {
* @returns {number} The edit distance between the two strings.
*/
function editDistance(s1: string, s2: string): number {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();

const costs = new Array<number>();
for (let i = 0; i <= s1.length; i++) {
let lastValue = i;
Expand Down
11 changes: 11 additions & 0 deletions tests/scripts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,17 @@ describe('#removeArabicAffixes', () => {
});

describe('#similarityScore', () => {
it.each([
['İ', 'a', 0],
['İ', 'i', 0.5],
['İ', '', 0],
['İ', 'i\u0307', 1],
['A', 'a', 1],
])('should score %j and %j as %s after case conversion in either order', (left, right, expected) => {
expect(ArabicServices.utils.similarityScore(left, right)).toBe(expected);
expect(ArabicServices.utils.similarityScore(right, left)).toBe(expected);
});

it('should return 1 for identical strings', () => {
const s1 = 'مرحبا';
const s2 = 'مرحبا';
Expand Down
Loading