Last active
June 17, 2020 18:23
-
-
Save erica/b39da92707987f8455f25f49c4678000 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension String { | |
/// Returns a new string with the camel-case-based words of this string | |
/// split by the specified separator. | |
/// | |
/// Examples: | |
/// | |
/// "myProperty".convertedToSnakeCase() | |
/// // my_property | |
/// "myURLProperty".convertedToSnakeCase() | |
/// // my_url_property | |
/// "myURLProperty".convertedToSnakeCase(separator: "-") | |
/// // my-url-property | |
func convertedToSnakeCase(separator: Character = "_") -> String { | |
guard !isEmpty else { return self } | |
return zip(self, indices) | |
.map({ (character: Character, idx: Index) -> String in | |
let lower = String(character).lowercased() | |
// Character is not uppercase or is start of string | |
if idx == startIndex || !character.isUppercase { return lower } | |
// Idx is not startIndex | |
let previousIdx = index(before: idx) | |
// An uppercase follows a non-uppercase/non-separator like P in myProperty, insert separator | |
if previousIdx != startIndex && !self[previousIdx].isUppercase && self[previousIdx] != separator { | |
return String(separator) + lower | |
} | |
let nextIdx = index(after: idx) | |
// Final letter _or_ next is uppercase, like L in myURL or R in myURLProperty, do not separate | |
if nextIdx == endIndex || self[nextIdx].isUppercase { | |
return lower | |
} | |
// Next is _not_ uppercase, previous _is_ uppercase, like P in myURLProperty. Insert separator | |
if self[previousIdx].isUppercase { | |
return String(separator) + lower | |
} | |
return lower | |
}).reduce("", +) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension String { | |
/// Returns a new string with the camel-case-based words of this string | |
/// split by the specified separator. | |
/// | |
/// Examples: | |
/// | |
/// "myProperty".convertedToSnakeCase() | |
/// // my_property | |
/// "myURLProperty".convertedToSnakeCase() | |
/// // my_url_property | |
/// "myURLProperty".convertedToSnakeCase(separator: "-") | |
/// // my-url-property | |
func convertedToSnakeCase(separator: Character = "_") -> String { | |
guard !isEmpty else { return self } | |
var result = "" | |
for (character, idx) in zip(self, indices) { | |
let lower = String(character).lowercased() | |
// Character is not uppercase or is start of string | |
if idx == startIndex || !character.isUppercase { | |
result += lower | |
continue | |
} | |
// An uppercase follows a non-uppercase/non-separator like P in myProperty, insert separator | |
if !self[index(before: idx)].isUppercase && | |
self[index(before: idx)] != separator { | |
result += String(separator) + lower | |
continue | |
} | |
let nextIdx = index(after: idx) | |
// Final letter _or_ next is uppercase, like L in myURL or R in myURLProperty, do not separate | |
if nextIdx == endIndex || self[nextIdx].isUppercase { | |
result += lower | |
continue | |
} | |
// Next is _not_ uppercase, previous _is_ uppercase, like P in myURLProperty. Insert separator | |
if self[index(before: idx)].isUppercase { | |
result += String(separator) + lower | |
continue | |
} | |
result += lower | |
} | |
return result | |
} | |
} | |
print("m_P_r_operty".convertedToSnakeCase(), | |
"myProperty".convertedToSnakeCase(), | |
"myURLProperty".convertedToSnakeCase(), | |
"myURLProperty".convertedToSnakeCase(separator: "-")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension String { | |
/// Returns a new string with the camel-case-based words of this string | |
/// split by the specified separator. | |
/// | |
/// Examples: | |
/// | |
/// "myProperty".convertedToSnakeCase() | |
/// // my_property | |
/// "myURLProperty".convertedToSnakeCase() | |
/// // my_url_property | |
/// "myURLProperty".convertedToSnakeCase(separator: "-") | |
/// // my-url-property | |
func convertedToSnakeCase(separator: Character = "_") -> String { | |
guard !isEmpty else { return self } | |
var result = "" | |
// Whether we should append a separator when we see a uppercase character. | |
var separateOnUppercase = true | |
for index in indices { | |
let nextIndex = self.index(after: index) | |
let character = self[index] | |
if character.isUppercase { | |
if separateOnUppercase && !result.isEmpty { | |
// Append the separator. | |
result += "\(separator)" | |
} | |
// If the next character is uppercase and the next-next character is lowercase, like "L" in "URLSession", we should separate words. | |
separateOnUppercase = nextIndex < endIndex && self[nextIndex].isUppercase && self.index(after: nextIndex) < endIndex && self[self.index(after: nextIndex)].isLowercase | |
} else { | |
// If the character is `separator`, we do not want to append another separator when we see the next uppercase character. | |
separateOnUppercase = character != separator | |
} | |
// Append the lowercased character. | |
result += character.lowercased() | |
} | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment