Reputation: 9586
I need to figure out a way to split strings at certain character sequences into an array of strings but the problem with my current implementation is that if there are two or more splits occurring in a string, it'll overwrite the former one.
Consider splitting the following string at st
and ck
...
stockwell
With regexp match I get two matches with ranges but if I try to assemble these to an array afterwards in a loop, the second match overwrites the first...
stockwell: (0,2) --> st --> _st_ockwell
stockwell: (3,2) --> ck --> sto_ck_well
What would be optimal for this is a String extension method that can split a string into a [String]
at several indices but I haven't been successful yet to come up with one.
Can somebody give me some help here?
Upvotes: 0
Views: 151
Reputation:
Another way to do this, it depends on making sure that your separators don't overlap or contain each other.
import Foundation
let initial = "stockwell"
let firstSeparator = "st"
let secondSeparator = "ck"
var separated = [String]()
let firstSplit = initial.componentsSeparatedByString(firstSeparator)
for next in [firstSeparator].join(firstSplit.map{[$0]}) {
let secondSplit = next.componentsSeparatedByString(secondSeparator)
separated += [secondSeparator].join(secondSplit.map{[$0]})
}
separated // => ["", "st", "o", "ck", "well"]
It works by separating the string into an array using the first delimiter, inserting the delimiter into the array between elements, then doing the same to every element in the new array with the next delimiter.
Upvotes: 1
Reputation: 10091
How about this:
extension String {
func multiSplit(inds: [String.Index]) -> [String] {
if let loc = inds.last {
return self[self.startIndex..<loc].multiSplit(Array(dropLast(inds))) + [self[loc..<self.endIndex]]
} else {
return [self]
}
}
}
let string = "abcdef"
let inds = [
string.startIndex.successor().successor(),
string.endIndex.predecessor().predecessor()
]
string.multiSplit(inds) // ["ab", "cd", "ef"]
The indices you give it have to be in order to work.
It's a recursive function - it takes the last index in the array of indices it's given (inds), and splits the string at that index. The first half of that split is recursively given to itself, with the last element of the array of indices removed. The "if let" fails if the index array is empty, so it just returns the string without any splits, but in an array of one.
In the example, It'll first split at the last index it's given, and get two strings: "abcd" and "ef". Then, the function calls itself with "abcd", and an array of indices without the last element (dropLast()). In this function, it'll split "abcd" into "ab" and "cd", and call itself again on "ab". But, since it passes an empty array to that function, the inds.last will fail, and it won't split anything, and just return ["ab"] (the line with return [self]). This goes up to the function above - which appends its split ("cd") onto it, and returns ["ab", "cd"]. This, finally, goes to the function above, which appends its split, and it returns the answer: ["ab, "cd", "ef"].
It's all just normal Swift, though, no imports or anything.
If you were dealing with indices out of order, you can just sort them:
let string = "abcdef"
let inds = [
string.endIndex.predecessor().predecessor(),
string.startIndex.successor().successor()
]
string.multiSplit(sorted(inds)) // ["ab", "cd", "ef"]
Also, if you want to remove empty strings from within the function:
extension String {
func multiSplit(inds: [String.Index]) -> [String] {
if let loc = inds.last {
return (
self[self.startIndex..<loc]
.multiSplit(Array(dropLast(inds)))
+ [self[loc..<self.endIndex]]
).filter{!$0.isEmpty}
} else {
return [self]
}
}
}
Upvotes: 1
Reputation: 13640
You can use lookahead and lookbehind for matching the positions:
(?=st)|(?<=st)|(?=ck)|(?<=ck)
And replace with _
See DEMO
Upvotes: 1