Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Last active February 29, 2024 16:16
Show Gist options
  • Save robertmryan/7fc421b905d22be5063528e64676529a to your computer and use it in GitHub Desktop.
Save robertmryan/7fc421b905d22be5063528e64676529a to your computer and use it in GitHub Desktop.
Swift 4 string extension to find all ranges of some substring
extension StringProtocol where Index == String.Index {
func ranges<T: StringProtocol>(of substring: T, options: String.CompareOptions = [], locale: Locale? = nil) -> [Range<Index>] {
var ranges: [Range<Index>] = []
while let result = range(of: substring, options: options, range: (ranges.last?.upperBound ?? startIndex)..<endIndex, locale: locale) {
ranges.append(result)
}
return ranges
}
}
@kk143g
Copy link

kk143g commented Feb 28, 2024

What if we need to find the exact word in string?
e.g

let myStr = "Administrator or worker in office"
let searchString = "or"
let results = string.ranges(of: searchString) --> Contains 2 ranges

here if we apply the above extension to find ranges of exact word then it will give 2 ranges (one from word Administrator and second itself "or" word), is there any way to find only the exact match of a word?
Background: I need to highlight the word "or" using attributed string, for that i need to find range of word "or".

@robertmryan
Copy link
Author

robertmryan commented Feb 28, 2024

@kk143g – Personally, I would use the new Regex regular expressions, where /…/ is a regex literal pattern, the pattern to be searched is between the / delimiters, and where we use \b to designate a “word boundary”:

let string = "Administrator or worker in office"

let pattern = /\bor\b/
    .ignoresCase()

for match in string.matches(of: pattern) {
    print(string[match.range])
}

Or, if you needed to support old OS versions, you can use the extension shown above and use the .regularExpression option:

let string = "Administrator or worker in office"

let ranges = string.ranges(of: #"\bor\b"#, options: [.regularExpression, .caseInsensitive])

for range in ranges {
    print(string[range])
}

For the sake of completeness, you could also use the NaturalLanguage framework, but that’s horrible overkill, IMHO.

@robertmryan
Copy link
Author

robertmryan commented Feb 28, 2024

Or also,

let string = "Administrator or worker in office"

let pattern = Regex {
    Anchor.wordBoundary
    "or"
    Anchor.wordBoundary
}.ignoresCase()

for match in string.matches(of: pattern) {
    print(string[match.range])
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment