Camel Case
August 29, 2017
This is easy if you take a naive view of the task:
(define (camel-case->underscore str)
(let loop ((cs (string->list str)) (zs (list)))
(if (null? cs) (list->string (reverse zs))
(if (char-upper-case? (car cs))
(loop (cdr cs) (cons (char-downcase (car cs)) (cons #\_ zs)))
(loop (cdr cs) (cons (car cs) zs))))))
(define (underscore->camel-case str)
(let loop ((cs (string->list str)) (zs (list)))
(if (null? cs) (list->string (reverse zs))
(if (char=? (car cs) #\_)
(if (null? (cdr cs)) (list->string (reverse zs))
(loop (cddr cs) (cons (char-upcase (cadr cs)) zs)))
(loop (cdr cs) (cons (car cs) zs))))))
Here are some examples:
> (camel-case->underscore "camelCaseToUnderscore") "camel_case_to_underscore" > (underscore->camel-case "underscore_to_camel_case") "underscoreToCamelCase" > (camel-case->underscore "HTTPClient") "_h_t_t_p_client" > (camel-case->underscore "getHTTP") "get_h_t_t_p"
The first two examples are fine. The last two are complicated. I suppose the desired answer is “http_client” for the third example and “get_http” for the fourth example, but that’s not clear (you might prefer “HTTP_client” and “get_HTTP”), and it’s hard to arrange. The question is what to do with successive capitals. In one case, the final capital is part of the same substring, in the other case it starts a new substring. We could make a special case for HTTP, but that’s a little bit limited. And if you require that a round trip from camel case to underscore and back results in the same string you started with, the situation gets even worse.
I found this question on a bulletin board of interview questions. I can only think that the purpose of the question is to fail any candidate who proposes an answer without asking for clarification.
You can run the program at https://ideone.com/9RzXoS.
This gives a slightly different answer (when the first character is upper case as it doesn’t prefix it with an underscore…;
sub from_cc { return join '_', map { $_ } split /(?=[A-Z])/, shift; } sub to_cc { my $t = join '', map { ucfirst lc $_ } split /_/, shift; substr $t,0,1,lc substr $t,0,1; return $t; }In Python. Probably the regex split can be improved to handle multiple capitals, but I did not manage yet.
def split_on_case(txt): return regex.split("(?V1)(?<=[a-z])(?=[A-Z])", txt) def split_on_case(txt): S, last = [], "" G = ["".join(g) for k, g in groupby(txt, str.isupper)] for g in G: if str.isupper(g): if g == G[-1]: S.append(g) elif len(g) > 1: S.append(g[:-1]) last = g[-1] else: last = g else: S.append("".join((last, g))) return S def camel_underscore(txt): elts = [w.lower() for w in split_on_case(txt)] return "_".join(elts) def underscore_camel(txt): elts = [w[0].upper() + w[1:] for w in txt.split("_")] return "".join(elts) print(camel_underscore("CamelCase")) print(underscore_camel("def_with_underscores")) print(camel_underscore("getHTPPClient")) print(camel_underscore("getHTPP")) print(underscore_camel("get_HTTP_client")) """ with lengthy split method camel_case DefWithUnderscores get_htpp_client get_htpp GetHTTPClient with regex camel_case DefWithUnderscores get_htppclient get_htpp GetHTTPClient """Why would anyone want to convert to the underscore format? Although this may be a very interesting exercise programmatically, I find its use case quite limited.
@Zack: Underscore case is the convention in some languages, including Oracle SQL and PL/SQL which I use every day in my regular job.
On Tue, Aug 29, 2017 at 9:08 AM, Programming Praxis wrote:
>
(import (scheme base) (srfi 1) (scheme char) (scheme write)) ;;; Naive versions, based on literal interpretation (define (camel->snake str) (list->string (reverse (fold (lambda (c lst) (if (char-upper-case? c) (cons (char-downcase c) (cons #\_ lst)) (cons c lst))) '() (string->list str))))) (camel->snake "fooBarBazItIs") (define (snake->camel str) (list->string (reverse (fold (lambda (c lst) (if (and (pair? lst) (char=? #\_ (car lst))) (cons (char-upcase c) (cdr lst)) (cons c lst))) '() (string->list str))))) (snake->camel (camel->snake "fooBarBazItIs")) ;;; A different version of camel->snake. ;; We will use the rule that an uppercase character X is treated as ;; special (i.e., it is subjected to the rewriting X -> _x) iff there ;; is a lowercase character immediately to the left or (inclusive) ;; right of X. (define (camel-hump? str idx) (and (char-upper-case? (string-ref str idx)) (or (and (> idx 0) (char-lower-case? (string-ref str (- idx 1)))) (and (< idx (- (string-length str) 1)) (char-lower-case? (string-ref str (+ idx 1))))))) (define (show-humps str) (map (lambda (i) (cons (string-ref str i) (camel-hump? str i))) (iota (string-length str)))) (define (camel->snake/grouping str) (apply string-append (list-tabulate (string-length str) (lambda (i) (if (camel-hump? str i) (string #\_ (string-ref str i)) (string (string-ref str i))))))) (map camel->snake/grouping '("HTTPClient" "getHTTP" "XMLServer" "XMLHTTPServer")) (map snake->camel (map camel->snake/grouping '("HTTPClient" "getHTTP" "XMLServer" "XMLHTTPServer"))) ;;;A shorter version of the split function.
def split_on_case(txt): program = re.compile("[A-Z]?[a-z]+") splits = set([0, len(txt)]) for m in program.finditer(txt): splits.update(m.span()) return [txt[b:e] for b, e in pairwise(sorted(splits))]Solution for ASCII characters in Ruby.
def camel_to_snake(camel_string) camel_string.gsub(/[a-z][A-Z]/) { |x| x.chars.join('_').downcase } end def snake_to_camel(snake_string) snake_string.gsub(/_[a-z]/) { |x| x.chars.last.upcase } endThe test…
Outputs:
Solution for ASCII characters in Ruby. (Posting again without Emojis)
def camel_to_snake(camel_string) camel_string.gsub(/[a-z][A-Z]/) { |x| x.chars.join('_').downcase } end def snake_to_camel(snake_string) snake_string.gsub(/_[a-z]/) { |x| x.chars.last.upcase } endThe test…
Outputs:
@programmingpraxis fell free to delete my previous post.