-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspellchecker9000.ts
executable file
·99 lines (82 loc) · 2.78 KB
/
spellchecker9000.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env ts-node
import * as fs from "fs";
import * as readline from "readline";
import { distance } from "fastest-levenshtein";
if (process.argv.length !== 4) {
console.log(
"Usage: ./spellchecker9000.ts <dictionary_path> <file_to_check_path>"
);
process.exit(1);
}
const dictionaryPath = process.argv[2];
const fileToCheckPath = process.argv[3];
const loadDictionary = (path: string): Promise<Set<string>> => {
const words = new Set<string>();
const fileStream = fs.createReadStream(path, { encoding: "utf-8" });
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity, // this always consider /r/n as a single line break
});
return new Promise((resolve, reject) => {
rl.on("line", (line) => {
words.add(line.trim().toLowerCase());
})
.on("close", () => {
resolve(words);
})
.on("error", (err) => {
reject(err);
});
});
};
const checkSpelling = async (dictionary: Set<string>, path: string) => {
const fileStream = fs.createReadStream(path, { encoding: "utf-8" });
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
const misspelledWords = new Set<string>();
let lineNumber = 0;
for await (const line of rl) {
lineNumber += 1;
const words = line.split(/\s+/); // split on any whitespace
words.forEach((word, index) => {
if (word.match(/\b[A-Z][a-z]*\b/)) return; // attempting to catch proper nouns 😅
const cleanWord = word.toLowerCase().replace(/[^a-z]/gi, ""); // remove non-alphabetic characters
if (cleanWord.length === 0) return; // skip empty strings (e.g. multiple spaces between words)
if (cleanWord === "a" || cleanWord === "i") return; // skip single letter words (a, i)
if (!dictionary.has(cleanWord)) {
misspelledWords.add(cleanWord);
console.log(
`Misspelled word: "${word}" at line ${lineNumber} column ${index + 1}`
);
console.log(line);
// suggestion
let minVal = Infinity;
const suggestions = Array.from(dictionary).reduce((acc, curr) => {
// find the min distance of any word in the dictionary
// return all words that have that same min distance
// in an array
const dist = distance(cleanWord, curr);
if (dist < minVal) {
minVal = dist;
acc = [curr];
} else if (dist === minVal) {
acc.push(curr);
}
return acc;
}, []);
console.log("Suggestions:", suggestions);
}
});
}
};
const main = async () => {
try {
const dictionary = await loadDictionary(dictionaryPath);
await checkSpelling(dictionary, fileToCheckPath);
} catch (error) {
console.error("Error:", error);
}
};
main();