Find index of Nth instance of substring in string in Swift
Xcode 11 • Swift 5 or later
let sentence = "hey hi hello, hey hi hello"
let query = "hello"
var searchRange = sentence.startIndex..<sentence.endIndex
var indices: [String.Index] = []
while let range = sentence.range(of: query, options: .caseInsensitive, range: searchRange) {
searchRange = range.upperBound..<searchRange.upperBound
indices.append(range.lowerBound)
}
print(indices) // "[7, 21]\n"
Another approach is NSRegularExpression
which is designed to easily iterate through matches in an string. And if you use the .ignoreMetacharacters
option, it will not apply any sophisticated wildcard/regex logic, but will just look for the string in question. So consider:
let string = "hey hi hello, hey hi hello" // string to search within
let searchString = "hello" // string to search for
let matchToFind = 2 // grab the second occurrence
let regex = try! NSRegularExpression(pattern: searchString, options: [.caseInsensitive, .ignoreMetacharacters])
You could use enumerateMatches
:
var count = 0
let range = NSRange(string.startIndex ..< string.endIndex, in: string)
regex.enumerateMatches(in: string, range: range) { result, _, stop in
count += 1
if count == matchToFind {
print(result!.range.location)
stop.pointee = true
}
}
Or you can just find all of them with matches(in:range:)
and then grab the n'th one:
let matches = regex.matches(in: string, range: range)
if matches.count >= matchToFind {
print(matches[matchToFind - 1].range.location)
}
Obviously, if you were so inclined, you could omit the .ignoreMetacharacters
option and allow the user to perform regex searches, too (e.g. wildcards, whole word searches, start of word, etc.).
For Swift 2, see previous revision of this answer.