Skip to content

Commit

Permalink
Allow different number of typos depending on filter length
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec committed Nov 6, 2024
1 parent 83d0302 commit f7bdbde
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 32 deletions.
4 changes: 2 additions & 2 deletions docs/user_guide/docs/goals.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ There are icons at the top of each column to
![Review Entries column sort icon](../images/reviewEntriesColumnSort.png){width=20} sort the data.

In a column with predominantly text content (Vernacular, Glosses, Note, or Flag), you can sort alphabetically or filter
with a text search.By default, the text search is a fuzzy match: it is not case sensitive and it allows for a single
typo. If you want exact text matches, use quotes around your filter.
with a text search. By default, the text search is a fuzzy match: it is not case sensitive and it allows for one or two
typos. If you want exact text matches, use quotes around your filter.

In the Number of Senses column or Pronunciations column, you can sort or filter by the number of senses or recordings
that entries have. In the Pronunciations column, you can also filter by speaker name.
Expand Down
4 changes: 2 additions & 2 deletions docs/user_guide/docs/goals.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ There are icons at the top of each column to
![Review Entries column sort icon](images/reviewEntriesColumnSort.png){width=20} sort the data.

In a column with predominantly text content (Vernacular, Glosses, Note, or Flag), you can sort alphabetically or filter
with a text search. By default, the text search is a fuzzy match: it is not case sensitive and it allows for a single
typo. If you want exact text matches, use quotes around your filter.
with a text search. By default, the text search is a fuzzy match: it is not case sensitive and it allows for one or two
typos. If you want exact text matches, use quotes around your filter.

In the Number of Senses column or Pronunciations column, you can sort or filter by the number of senses or recordings
that entries have. In the Pronunciations column, you can also filter by speaker name.
Expand Down
4 changes: 2 additions & 2 deletions docs/user_guide/docs/goals.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ There are icons at the top of each column to
![Review Entries column sort icon](../images/reviewEntriesColumnSort.png){width=20} sort the data.

In a column with predominantly text content (Vernacular, Glosses, Note, or Flag), you can sort alphabetically or filter
with a text search. By default, the text search is a fuzzy match: it is not case sensitive and it allows for a single
typo. If you want exact text matches, use quotes around your filter.
with a text search. By default, the text search is a fuzzy match: it is not case sensitive and it allows for one or two
typos. If you want exact text matches, use quotes around your filter.

In the Number of Senses column or Pronunciations column, you can sort or filter by the number of senses or recordings
that entries have. In the Pronunciations column, you can also filter by speaker name.
Expand Down
22 changes: 11 additions & 11 deletions src/goals/ReviewEntries/ReviewEntriesTable/filterFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ export function isQuoted(filter: string): boolean {
return /^["'\p{Pi}].*["'\p{Pf}]$/u.test(filter);
}

/** Number of typos allowed, depending on filter-length. */
function levDist(len: number): number {
return len < 3 ? 0 : len < 6 ? 1 : 2;
}

/** Checks if value contains a substring that fuzzy-matches the filter. */
export function fuzzyContains(
value: string,
filter: string,
levenshteinDistance = 1
): boolean {
export function fuzzyContains(value: string, filter: string): boolean {
filter = filter.toLowerCase();
value = value.toLowerCase();
// `fuzzySearch(...)` returns a generator;
// `.next()` on a generator always returns an object with boolean property `done`
return !fuzzySearch(
filter.toLowerCase(),
value.toLowerCase(),
levenshteinDistance
).next().done;
return !fuzzySearch(filter, value, levDist(filter.length)).next().done;
}

/** Check if string matches filter. */
/** Check if string matches filter.
* If filter quoted, exact match. Otherwise, fuzzy match. */
export function matchesFilter(value: string, filter: string): boolean {
filter = filter.trim();
return isQuoted(filter)
Expand Down
30 changes: 15 additions & 15 deletions src/goals/ReviewEntries/ReviewEntriesTable/tests/filterFn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,30 @@ describe("filterFn", () => {

describe("fuzzyContains", () => {
const testString = "I am a string with many possible substrings.";
const exactMatch = ["I am", "strin", "ny possi"];
const fuzzyMatch = ["Iam", "strink", "nt possi"];
const nonMatch = ["I'm", "strinket", "ny ssi"];

test("Levenshtein distance 0", () => {
exactMatch.forEach((s) =>
expect(ff.fuzzyContains(testString, s, 0)).toBeTruthy()
);
fuzzyMatch.forEach((s) =>
expect(ff.fuzzyContains(testString, s, 0)).toBeFalsy()
test("Short: no typos allowed", () => {
["i", "am", "a s"].forEach((s) =>
expect(ff.fuzzyContains(testString, s)).toBeTruthy()
);
nonMatch.forEach((s) =>
expect(ff.fuzzyContains(testString, s, 0)).toBeFalsy()
["@", "aq"].forEach((s) =>
expect(ff.fuzzyContains(testString, s)).toBeFalsy()
);
});

test("Levenshtein distance 1 (default)", () => {
exactMatch.forEach((s) =>
test("Medium: 1 typo allowed", () => {
["i b", "ama", "strim"].forEach((s) =>
expect(ff.fuzzyContains(testString, s)).toBeTruthy()
);
fuzzyMatch.forEach((s) =>
["i'm", "astrr"].forEach((s) =>
expect(ff.fuzzyContains(testString, s)).toBeFalsy()
);
});

test("Long: 2 typos allowed", () => {
["i'm a string", "with man88"].forEach((s) =>
expect(ff.fuzzyContains(testString, s)).toBeTruthy()
);
nonMatch.forEach((s) =>
["i'm a ztring", "with man888"].forEach((s) =>
expect(ff.fuzzyContains(testString, s)).toBeFalsy()
);
});
Expand Down

0 comments on commit f7bdbde

Please sign in to comment.