I believe the most efficient way would be to iterate over the characters :
let str = "First Middle Last"
let beforeLast = str.isEmpty ? str.startIndex : str.index(before: str.endIndex)
var result = ""
var c = str.startIndex
OuterLoop: do {
while c < str.endIndex {
while str[c] == " " {
if c < beforeLast { c = str.index(after: c) }
else { break OuterLoop }
}
result.append(str[c])
while str[c] != " " {
if c < beforeLast { c = str.index(after: c) }
else { break OuterLoop }
}
}
}
print(result) //"FML"
This is more efficient than creating an intermediary array of single words, and then prefix the elements and cast the prefix to String, and then join the elements of that new array.
Try It Online! (only take results with higher CPU share into account, and run codes separately)
Here are some benchmarks:
@RamyMohamed's solution : 0.000708s
@DávidPásztor's 2nd solution : 0.000669s //separatedBy: CharacterSet.whitespacesAndNewlines
@AhmadF's solution : 0.000561s
@DávidPásztor's 1st solution : 0.000550 //separatedBy: " "
This solution : 0.000110s
If you'd like to take into account newlines ant tabulations, a simple solution is to define an extension on Character
:
extension Character {
static let blanks = [" ", "\n", "\t", "\r"]
func isBlank() -> Bool {
return Character.blanks.contains(String(self))
}
}
and use it like so :
OuterLoop: do {
while c < str.endIndex {
while str[c].isBlank() {
if c < beforeLast { c = str.index(after: c) }
else { break OuterLoop }
}
result.append(str[c])
while !str[c].isBlank() {
if c < beforeLast { c = str.index(after: c) }
else { break OuterLoop }
}
}
}
It clocks at 0.000174s