5

I have a string extension:

extension String { func split(usingRegex pattern: String) -> [String] { let regex = try! NSRegularExpression(pattern: pattern) let matches = regex.matches(in: self, range: NSRange(0..<utf16.count)) let ranges = [startIndex..<startIndex] + matches.map{Range($0.range, in: self)!} + [endIndex..<endIndex] return (0...matches.count).map {String(self[ranges[$0].upperBound..<ranges[$0+1].lowerBound])} } } 

and I use it like this:

var string = "Hello45playground23today" var output = string.split(usingRegex: "[0-9]+") 

the output is:

["Hello", "playground", "today"] 

But what I need is:

["Hello", "45", "playground", "23", "today"] 

Is there a way to achieve that in Swift?

1
  • Are regular expressions an option? Commented Jul 26, 2019 at 8:22

1 Answer 1

10

Your code adds only the substrings between the matches (and before the first match and after the last match) to the result. What you need is also the substrings for the matches themselves. This can be done by creating an array with all indices where a match starts or ends, and then taking all substrings between consecutive indices:

extension String { func split(usingRegex pattern: String) -> [String] { let regex = try! NSRegularExpression(pattern: pattern) let matches = regex.matches(in: self, range: NSRange(startIndex..., in: self)) let splits = [startIndex] + matches .map { Range($0.range, in: self)! } .flatMap { [ $0.lowerBound, $0.upperBound ] } + [endIndex] return zip(splits, splits.dropFirst()) .map { String(self[$0 ..< $1])} } } 

Example:

let string = "Hello45playground23today" let output = string.split(usingRegex: "[0-9]+") print(output) // ["Hello", "45", "playground", "23", "today"] 

The same can be done with an explicit loop (less sophisticated, but perhaps better readable):

extension String { func split(usingRegex pattern: String) -> [String] { let regex = try! NSRegularExpression(pattern: pattern) let matches = regex.matches(in: self, range: NSRange(startIndex..., in: self)) var result: [String] = [] var pos = startIndex for match in matches { let range = Range(match.range, in: self)! result.append(String(self[pos..<range.lowerBound])) result.append(String(self[range])) pos = range.upperBound } result.append(String(self[pos...])) return result } } 
Sign up to request clarification or add additional context in comments.

1 Comment

Great answer. @BartłomiejSemańczyk For the fun of it, you could also use a simple regex but only for strings with same-length numbers: (?<=\d{2})|(?=\d{2})

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.