forked from nicklockwood/ShapeScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathString+Matching.swift
More file actions
61 lines (59 loc) · 2.34 KB
/
String+Matching.swift
File metadata and controls
61 lines (59 loc) · 2.34 KB
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
//
// String+Matching.swift
// ShapeScript Lib
//
// Created by Nick Lockwood on 14/09/2021.
// Copyright © 2021 Nick Lockwood. All rights reserved.
//
extension String {
/// Return the closest matches for the string in the given list of options
public func bestMatches(in options: [String]) -> [String] {
let lowercaseQuery = lowercased()
// Sort matches by Levenshtein edit distance
return options
.compactMap { option -> (String, distance: Int, commonPrefix: Int)? in
guard option != self else { return nil }
let lowercaseOption = option.lowercased()
let distance = lowercaseOption.editDistance(from: lowercaseQuery)
let commonPrefix = lowercaseOption.commonPrefix(with: lowercaseQuery)
if commonPrefix.isEmpty, distance > lowercaseQuery.count / 2 {
return nil
}
return (option, distance, commonPrefix.count)
}
.sorted {
if $0.distance == $1.distance {
return $0.commonPrefix > $1.commonPrefix
}
return $0.distance < $1.distance
}
.map { $0.0 }
}
/// The Damerau-Levenshtein edit-distance between two strings
func editDistance(from other: String) -> Int {
let lhs = Array(self)
let rhs = Array(other)
var dist = [[Int]]()
for i in stride(from: 0, through: lhs.count, by: 1) {
dist.append([i])
}
for j in stride(from: 1, through: rhs.count, by: 1) {
dist[0].append(j)
}
for i in stride(from: 1, through: lhs.count, by: 1) {
for j in stride(from: 1, through: rhs.count, by: 1) {
if lhs[i - 1] == rhs[j - 1] {
dist[i].append(dist[i - 1][j - 1])
} else {
dist[i].append(Swift.min(dist[i - 1][j] + 1,
dist[i][j - 1] + 1,
dist[i - 1][j - 1] + 1))
}
if i > 1, j > 1, lhs[i - 1] == rhs[j - 2], lhs[i - 2] == rhs[j - 1] {
dist[i][j] = Swift.min(dist[i][j], dist[i - 2][j - 2] + 1)
}
}
}
return dist[lhs.count][rhs.count]
}
}