Skip to content

Commit 1feefa1

Browse files
committed
feat: keyList sort by word-like
1 parent 946dce5 commit 1feefa1

File tree

2 files changed

+58
-9
lines changed

2 files changed

+58
-9
lines changed

cpp/JSMdict.cpp

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,56 @@ namespace JSMdict {
8989
});
9090
}
9191

92+
std::string toLower(std::string_view str) {
93+
std::string result = std::string(str);
94+
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
95+
return result;
96+
}
97+
98+
int levenshteinDistance(const std::string &s1, const std::string &s2) {
99+
size_t len1 = s1.size(), len2 = s2.size();
100+
std::vector<std::vector<int>> dp(len1 + 1, std::vector<int>(len2 + 1));
101+
102+
for (size_t i = 0; i <= len1; ++i) dp[i][0] = i;
103+
for (size_t j = 0; j <= len2; ++j) dp[0][j] = j;
104+
105+
for (size_t i = 1; i <= len1; ++i) {
106+
for (size_t j = 1; j <= len2; ++j) {
107+
dp[i][j] = std::min({dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (s1[i - 1] != s2[j - 1])});
108+
}
109+
}
110+
111+
return dp[len1][len2];
112+
}
113+
114+
int computeScore(const std::string &candidateOriginal, const std::string &queryOriginal) {
115+
std::string candidate = toLower(candidateOriginal);
116+
std::string query = toLower(queryOriginal);
117+
118+
if (candidate == query) return 100;
119+
if (candidate.starts_with(query)) return 80;
120+
if (candidate.find(query) != std::string::npos) return 50;
121+
return std::max(0, 100 - levenshteinDistance(candidate, query) * 10);
122+
}
123+
124+
std::vector<JSVariant> sortKeyListByKeywordRelevance(std::vector<mdict::key_list_item *> &items, const std::string &query) {
125+
std::partial_sort(
126+
items.begin(),
127+
items.begin() + 10,
128+
items.end(),
129+
[&query](const mdict::key_list_item *a, const mdict::key_list_item *b) {
130+
int scoreA = computeScore(a->key_word, query);
131+
int scoreB = computeScore(b->key_word, query);
132+
return scoreA > scoreB || (scoreA == scoreB && a->record_start < b->record_start);
133+
}
134+
);
135+
items.resize(10);
136+
auto list = std::vector<JSVariant>();
137+
for (auto item: items) list.push_back(JSVariant(item->key_word));
138+
return list;
139+
}
140+
141+
92142
jsi::Value JSMdict::keyList(jsi::Runtime &runtime, std::string query) {
93143
checkInitialized(runtime);
94144
return Promise::createPromise(
@@ -97,17 +147,16 @@ namespace JSMdict {
97147
[this, query](const std::shared_ptr<Promise> &promise) {
98148
threadPool.cancel(currentSearchTaskId);
99149
currentSearchTaskId = threadPool.enqueue([this, promise, query]() {
100-
auto list = std::vector<JSVariant>();
150+
auto tmp = std::vector<mdict::key_list_item *>();
101151
auto keys = mdict->keyList();
102-
for (auto &key: keys) {
103-
if (key->key_word.find(query) != std::string::npos) {
104-
list.insert(list.end(), JSVariant(key->key_word));
105-
}
106-
}
107-
promise->resolve(JSVariant(list));
152+
std::copy_if(keys.begin(), keys.end(), std::back_inserter(tmp), [&query](auto key) {
153+
return key->key_word.find(query) != std::string::npos;
154+
});
155+
keys.clear();
156+
promise->resolve(JSVariant(sortKeyListByKeywordRelevance(tmp, query)));
157+
tmp.clear();
108158
});
109159
});
110160
}
111-
112161
}
113162

example/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default function App() {
1414
}, []);
1515

1616
const onSearch = useCallback(async () => {
17-
const res = await mdict.current?.lookup('ab');
17+
const res = await mdict.current?.keyList('aban');
1818
console.log(res);
1919
}, []);
2020

0 commit comments

Comments
 (0)