Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separating CamelCase string into space-separated words in Swift

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.

like image 614
Brian Arnold Avatar asked Dec 22 '16 22:12

Brian Arnold


2 Answers

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 :)

like image 62
Augustine P A Avatar answered Sep 28 '22 17:09

Augustine P A


One Line Solution

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!

like image 39
AmitaiB Avatar answered Sep 28 '22 17:09

AmitaiB