-
-
Save dmsl1805/ad9a14b127d0409cf9621dc13d237457 to your computer and use it in GitHub Desktop.
extension String { | |
func snakeCased() -> String? { | |
let pattern = "([a-z0-9])([A-Z])" | |
let regex = try? NSRegularExpression(pattern: pattern, options: []) | |
let range = NSRange(location: 0, length: count) | |
return regex?.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "$1_$2").lowercased() | |
} | |
} |
This is an awesome string extension, good stuff. Using for consistency in my analytics
Nice fix for acronyms!
One more case to consider:
print("this1234".camelCaseToSnakeCase())
will output
this1234
Fix:
extension String {
func camelCaseToSnakeCase() -> String {
let acronymPattern = "([A-Z]+)([A-Z][a-z]|[0-9])"
let fullWordsPattern = "([a-z])([A-Z]|[0-9])"
let digitsFirstPattern = "([0-9])([A-Z])"
return self.processCamelCaseRegex(pattern: acronymPattern)?
.processCamelCaseRegex(pattern: fullWordsPattern)?
.processCamelCaseRegex(pattern:digitsFirstPattern)?.lowercased() ?? self.lowercased()
}
fileprivate func processCamelCaseRegex(pattern: String) -> String? {
let regex = try? NSRegularExpression(pattern: pattern, options: [])
let range = NSRange(location: 0, length: count)
return regex?.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "$1_$2")
}
}
If you don't want to pollute the Foundation namespace by introducing a String extension (which is fine if you are writing an app, but not encouraged if you are writing a library), here's the function version that can be placed in a string utility:
private func flattenCamalCase(string: String, separator: String = " ") -> String {
let acronymPattern = "([A-Z]+)([A-Z][a-z]|[0-9])"
let normalPattern = "([a-z0-9])([A-Z])"
if
let processedString = processCamalCaseRegex(string: string, pattern: acronymPattern, separator: separator),
let finalString = processCamalCaseRegex(string: processedString, pattern: normalPattern, separator: separator)?.lowercased()
{
return finalString
} else {
return string.lowercased()
}
}
private func processCamalCaseRegex(string: String, pattern: String, separator: String = " ") -> String? {
let regex = try? NSRegularExpression(pattern: pattern, options: [])
let range = NSRange(location: 0, length: string.count)
return regex?.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "$1\(separator)$2")
}
@superarts Why? if your code is private, even extensions can't be seen or used, as far as I checked.
@yoni-placer this is totally subjective. It's all up to what coding standard you set for yourself. If I'm writing a hobby project, I'd totally go with whatever String extension to make my life easier. However, if I'm working on a project with reusability in mind, I'd consider some scenarios and evaluate the technical approach.
Let's say I'm writing a shared framework that's used across multiple projects. I want flattenCamalCase
function implemented somewhere in my framework. If I would put it in a extension String
, what if one of my downstream projects uses another framework which also has a function called flattenCamalCase
in its own string extension, and both implementation are producing different results, and in one of the files of my downstream project, both frameworks need to be imported?
It sounds like I'm describing an impossible use case, but to be honest, if every framework was trying to put these "helper" functions inside string extensions, you would see a lot of namespace conflicts. The solution is quite simple though: don't do this in a reusable components. Instead, put it under MyFramework.StringUtility.flattenCamalCase
as I described, so it's under a specific namespace.
Another problem is dependency management. I used to be a big fan of system class extension myself, as I just needed the write the minimum lines of code. But now when I think about it, I can't justify the fact that just to save a function parameter (self
in extension, first parameter in a utility class/struct/enum), I need to make flattenCamalCase
global. When you said private
, I assume you didn't mean the technical term of private
, as your string extension is very likely to be internal
. And by doing that, you care making the whole function flattenCamalCase
global across your whole project, which is as bad as using global functions and variables. Again, if it's a hobby project I wouldn't pay too much attention about dependency injection, but for a commercial project, I can't imagine a 5 or 10 developers team manage a codebase without some sort of DI.
I'm writing all these not just to answer your question. In the end, it's personal preference and is totally subjective. But if you really care about SOLID principles, which by the way are pretty "solid", this kind of discussions are quite helpful to clear up our minds. So always agree to disagree :)
just use fileprivate extension
My own take on the thing:
https://gist.github.com/CanTheAlmighty/18b443a09ebe56c3cf78bc4785438cf8
Tweaked this a little bit to handle acronyms and single letter words:
input:
output: