Skip to content

Commit b9d4492

Browse files
committed
suggestion_list: Add early exit for distances above threshold
Replicates graphql/graphql-js@21e544d
1 parent aedb381 commit b9d4492

File tree

2 files changed

+25
-6
lines changed

2 files changed

+25
-6
lines changed

src/graphql/pyutils/suggestion_list.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Collection, List
1+
from typing import Collection, Optional, List
22

33
__all__ = ["suggestion_list"]
44

@@ -14,9 +14,9 @@ def suggestion_list(input_: str, options: Collection[str]) -> List[str]:
1414

1515
input_threshold = len(input_) // 2
1616
for option in options:
17-
distance = lexical_distance.measure(option)
1817
threshold = max(input_threshold, len(option) // 2, 1)
19-
if distance <= threshold:
18+
distance = lexical_distance.measure(option, threshold)
19+
if distance is not None:
2020
options_by_distance[option] = distance
2121

2222
# noinspection PyShadowingNames
@@ -46,7 +46,7 @@ def __init__(self, input_: str):
4646
row_size = len(input_) + 1
4747
self._rows = [[0] * row_size, [0] * row_size, [0] * row_size]
4848

49-
def measure(self, option: str):
49+
def measure(self, option: str, threshold: int) -> Optional[int]:
5050
if self._input == option:
5151
return 0
5252

@@ -59,6 +59,9 @@ def measure(self, option: str):
5959
a, b = option_lower_case, self._input_lower_case
6060
a_len, b_len = len(a), len(b)
6161

62+
if abs(a_len - b_len) > threshold:
63+
return None
64+
6265
rows = self._rows
6366
for j in range(0, b_len + 1):
6467
rows[0][j] = j
@@ -67,7 +70,7 @@ def measure(self, option: str):
6770
up_row = rows[(i - 1) % 3]
6871
current_row = rows[i % 3]
6972

70-
current_row[0] = i
73+
smallest_cell = current_row[0] = i
7174
for j in range(1, b_len + 1):
7275
cost = 0 if a[i - 1] == b[j - 1] else 1
7376

@@ -82,6 +85,15 @@ def measure(self, option: str):
8285
double_diagonal_cell = rows[(i - 2) % 3][j - 2]
8386
current_cell = min(current_cell, double_diagonal_cell + 1)
8487

88+
if current_cell < smallest_cell:
89+
smallest_cell = current_cell
90+
8591
current_row[j] = current_cell
8692

87-
return rows[a_len % 3][b_len]
93+
# Early exit, since distance can't go smaller than smallest element
94+
# of the previous row.
95+
if smallest_cell > threshold:
96+
return None
97+
98+
distance = rows[a_len % 3][b_len]
99+
return distance if distance <= threshold else None

tests/pyutils/test_suggestion_list.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ def returns_options_with_small_lexical_distance():
1818
expect_suggestions("greenish", ["green"], ["green"])
1919
expect_suggestions("green", ["greenish"], ["greenish"])
2020

21+
def rejects_options_with_distance_that_exceeds_threshold():
22+
expect_suggestions("aaaa", ["aaab"], ["aaab"])
23+
expect_suggestions("aaaa", ["aabb"], ["aabb"])
24+
expect_suggestions("aaaa", ["abbb"], [])
25+
26+
expect_suggestions("ab", ["ca"], [])
27+
2128
def returns_options_with_different_case():
2229
expect_suggestions("verylongstring", ["VERYLONGSTRING"], ["VERYLONGSTRING"])
2330

0 commit comments

Comments
 (0)