Split String in Swift by their capital letters
A different solution in Functional Programming style
isUppercase
First of all lets define an easy method to check whether a Character is uppercase
extension Character {
var isUppercase: Bool { return String(self).uppercased() == String(self) }
}
Indexes
Next we need the indexes of the uppercase characters
let indexes = Set(text
.characters
.enumerated()
.filter { $0.element.isUppercase }
.map { $0.offset })
Building the result
Now we can build the result
let chunks = text
.characters
.map { String($0) }
.enumerated()
.reduce([String]()) { chunks, elm -> [String] in
guard !chunks.isEmpty else { return [elm.element] }
guard !indexes.contains(elm.offset) else { return chunks + [String(elm.element)] }
var chunks = chunks
chunks[chunks.count-1] += String(elm.element)
return chunks
}
Output
["Na", "Cu", "H", "He"]
(Swift 3)
We could let ourselves be inspired by the implementation of the split
function in Sequence
, and implement our own splitBefore
method (split before separator, omitting empty subsequences), that keep the separators in the splitted sequence.
extension Sequence {
func splitBefore(
separator isSeparator: (Iterator.Element) throws -> Bool
) rethrows -> [AnySequence<Iterator.Element>] {
var result: [AnySequence<Iterator.Element>] = []
var subSequence: [Iterator.Element] = []
var iterator = self.makeIterator()
while let element = iterator.next() {
if try isSeparator(element) {
if !subSequence.isEmpty {
result.append(AnySequence(subSequence))
}
subSequence = [element]
}
else {
subSequence.append(element)
}
}
result.append(AnySequence(subSequence))
return result
}
}
Used as follows
/* help property */
extension Character {
var isUpperCase: Bool { return String(self) == String(self).uppercased() }
}
/* example usage */
let teststring = "NaCuHHe"
let splitted = teststring
.characters
.splitBefore(separator: { $0.isUpperCase })
.map{String($0)}
print(splitted) // ["Na", "Cu", "H", "He"]