Reputation: 25
I am creating a chat feature for my app and we have a set of custom emojis.
If the user types the shortcut for one of those emotes in their comment, we need to be able to detect the shortcuts in order of appearance and replace them with the proper emote.
For example.
let emotes = [
"app" : "[]"
"happy" : ":)",
]
let userComment = "I am happy"
let expectedResult = "I am :)" // "app" was not replaced because "happy"'s "h" was found first, hence it was evaluated before "app"
Considering that multiple occurrences of a same emote can appear in a single comment, is there an efficient algorithm to achieve this?
I know there is range(of:)
to find the range of a substring, but using it seems super inefficient considering that we might have dozens of emojis.
UPDATE:
Please be aware that the emotes "keys" (shortcuts) may collide, so iterating over the emotes dictionary would not be the answer. I changed the sample code to reflect this.
Upvotes: 1
Views: 107
Reputation: 540055
A regular expression can be used to find all occurrences of any of the dictionary keys, for example
(app|happy)
The word-boundary pattern \b
can be used to match only complete words:
\b(app|happy)\b
Combining this approach with the code from Replace matching regex values in string with correct value from dictionary we get the following implementation:
func replaceOccurrences(in string: String, fromDict dict: [String: String]) -> String {
var result = string
// A pattern matching any of the dictionary keys.
let pattern = "\\b(" + dict.keys.map { NSRegularExpression.escapedPattern(for: $0)}
.joined(separator: "|") + ")\\b"
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: string, range: NSRange(string.startIndex..., in: string))
// Iterate in reverse to avoid messing up the ranges as the keys are replaced with the values.
for match in matches.reversed() {
if let range = Range(match.range, in: string) {
// Extract key, look up replacement in dictionary, and replace in result string.
if let replacement = dict[String(string[range])] {
result.replaceSubrange(range, with: replacement)
}
}
}
return result
}
Example 1:
let emotes = [
"happy" : ":)",
"sad" : ":("
]
let userComment = "I am happy to meet you, but I am sad cos of this algorithm"
let result = replaceOccurrences(in: userComment, fromDict: emotes)
print(result) // I am :) to meet you, but I am :( cos of this algorithm
Example 2:
let emotes = [
"app" : "[]",
"happy" : ":)"
]
let userComment = "I am happy!"
let result = replaceOccurrences(in: userComment, fromDict: emotes)
print(result) // I am :)!
Upvotes: 1