I would like to separate a CamelCase string into space-separated words in a new string. Here is what I have so far:
var camelCaps: String { guard self.count > 0 else { return self } var newString: String = "" let uppercase = CharacterSet.uppercaseLetters let first = self.unicodeScalars.first! newString.append(Character(first)) for scalar in self.unicodeScalars.dropFirst() { if uppercase.contains(scalar) { newString.append(" ") } let character = Character(scalar) newString.append(character) } return newString } let aCamelCaps = "aCamelCaps" let camelCapped = aCamelCaps.camelCaps // Produce: "a Camel Caps" let anotherCamelCaps = "ÄnotherCamelCaps" let anotherCamelCapped = anotherCamelCaps.camelCaps // "Änother Camel Caps"
I'm inclined to suspect that this may not be the most efficient way to convert to space-separated words, if I call it in a tight loop, or 1000's of times. Are there more efficient ways to do this in Swift?
[Edit 1:] The solution I require should remain general for Unicode scalars, not specific to Roman ASCII "A..Z".
[Edit 2:] The solution should also skip the first letter, i.e. not prepend a space before the first letter.
[Edit 3:] Updated for Swift 4 syntax, and added caching of uppercaseLetters, which improves performance in very long strings and tight loops.
extension String { func camelCaseToWords() -> String { return unicodeScalars.dropFirst().reduce(String(prefix(1))) { return CharacterSet.uppercaseLetters.contains($1) ? $0 + " " + String($1) : $0 + String($1) } } } print("ÄnotherCamelCaps".camelCaseToWords()) // Änother Camel Caps
May be helpful for someone :)
I concur with @aircraft, regular expressions can solve this problem in one LOC!
// Swift 5 (and probably 4?) extension String { func titleCase() -> String { return self .replacingOccurrences(of: "([A-Z])", with: " $1", options: .regularExpression, range: range(of: self)) .trimmingCharacters(in: .whitespacesAndNewlines) .capitalized // If input is in llamaCase } }
Props to this JS answer.
P.S. I have a gist for snake_case → CamelCase
here.
P.P.S. I updated this for New Swift (currently 5.1), then saw @busta's answer, and swapped out my startIndex..<endIndex
for his range(of: self)
. Credit where it's due y'all!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With