How to sort an array of string by similarity to specific key
You can define your own similarity sorting method. Note that I have also added a hasPrefix priority over the ones which only contains the keyword which you can just remove if you don't want it:
var theArray = ["chill", "nifty", "precooled", "cooldaddy", "cool", "coolguy", "dandy", "uncool"]
let key = "cool"
let sorted = theArray.sorted {
if $0 == key && $1 != key {
return true
}
else if $0.hasPrefix(key) && !$1.hasPrefix(key) {
return true
}
else if !$0.hasPrefix(key) && $1.hasPrefix(key) {
return false
}
else if $0.hasPrefix(key) && $1.hasPrefix(key)
&& $0.count < $1.count {
return true
}
else if $0.contains(key) && !$1.contains(key) {
return true
}
else if !$0.contains(key) && $1.contains(key) {
return false
}
else if $0.contains(key) && $1.contains(key)
&& $0.count < $1.count {
return true
}
return false
}
print(sorted) // ["cool", "coolguy", "cooldaddy", "uncool", "precooled", "chill", "nifty", "dandy"]
You can also extend Sequence and create a sorted by key similarity method:
extension Sequence where Element: StringProtocol {
func sorted<S>(by key: S) -> [Element] where S: StringProtocol {
sorted {
if $0 == key && $1 != key {
return true
}
else if $0.hasPrefix(key) && !$1.hasPrefix(key) {
return true
}
else if !$0.hasPrefix(key) && $1.hasPrefix(key) {
return false
}
else if $0.hasPrefix(key) && $1.hasPrefix(key)
&& $0.count < $1.count {
return true
}
else if $0.contains(key) && !$1.contains(key) {
return true
}
else if !$0.contains(key) && $1.contains(key) {
return false
}
else if $0.contains(key) && $1.contains(key)
&& $0.count < $1.count {
return true
}
return false
}
}
}
let sorted = theArray.sorted(by: key) // "cool", "coolguy", "cooldaddy", "uncool", "precooled", "chill", "nifty", "dandy"]
And the mutating version as well:
extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {
mutating func sort<S>(by key: S) where S: StringProtocol {
sort {
if $0 == key && $1 != key {
return true
}
else if $0.hasPrefix(key) && !$1.hasPrefix(key) {
return true
}
else if !$0.hasPrefix(key) && $1.hasPrefix(key) {
return false
}
else if $0.hasPrefix(key) && $1.hasPrefix(key)
&& $0.count < $1.count {
return true
}
else if $0.contains(key) && !$1.contains(key) {
return true
}
else if !$0.contains(key) && $1.contains(key) {
return false
}
else if $0.contains(key) && $1.contains(key)
&& $0.count < $1.count {
return true
}
return false
}
}
}
First you need a measure of how similar two strings are. Here's a simple example:
extension String {
func equalityScore(with string: String) -> Double {
if self == string {
return 2 // the greatest equality score this method can give
} else if self.contains(string) {
return 1 + 1 / Double(self.count - string.count) // contains our term, so the score will be between 1 and 2, depending on number of letters.
} else {
// you could of course have other criteria, like string.contains(self)
return 1 / Double(abs(self.count - string.count))
}
}
}
Once you have that, you can use it to sort the array:
var theArray: [String] = ["uncool", "chill", "nifty", "precooled", "dandy", "cool"]
var compareString = "cool"
theArray.sort { lhs, rhs in
return lhs.equalityScore(with: compareString) > rhs.equalityScore(with: compareString)
}
Result: ["cool", "uncool", "precooled", "chill", "nifty", "dandy"]