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.

Advertisements

Pages: 1 2

8 Responses to “Camel Case”

  1. 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;
    }
    
  2. Paul said

    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
    """
    
  3. Zack said

    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.

  4. programmingpraxis said

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

    >

  5. chaw said

    (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")))
    
    ;;;
    

  6. Paul said

    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))]
    
  7. V said

    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 }
    end
    
    

    The test…

    
    def test(fn, input, target)
      output = self.send(fn, input)
      puts "%-17s : %-17s %s" % [input, output, (target == output ? '✅' : '❌')]
    end
    
    puts "Snake to Camel case"
    test :camel_to_snake, 'one',             'one'             
    test :camel_to_snake, 'camelCase',       'camel_case'      
    test :camel_to_snake, 'likeThis',        'like_this'       
    test :camel_to_snake, 'doTheEvolution',  'do_the_evolution'
    
    puts
    puts "Camel to Snake case"
    test :snake_to_camel, 'one',             'one'             
    test :snake_to_camel, 'camel_case',      'camelCase'      
    test :snake_to_camel, 'like_this',       'likeThis'       
    test :snake_to_camel, 'do_the_evolution', 'doTheEvolution'
      
    

    Outputs:

    
    Snake to Camel case
    one               : one               ✅
    camelCase         : camel_case        ✅
    likeThis          : like_this         ✅
    doTheEvolution    : do_the_evolution  ✅
    
    Camel to Snake case
    one               : one               ✅
    camel_case        : camelCase         ✅
    like_this         : likeThis          ✅
    do_the_evolution  : doTheEvolution    ✅
    
    
  8. V said

    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 }
    end
    
    

    The test…

    
    def test(fn, input, target)
      output = self.send(fn, input)
      puts "%-17s : %-17s %s" % [input, output, (target == output ? 'OK' : 'FAIL')]
    end
    
    puts "Snake to Camel case"
    test :camel_to_snake, 'one',             'one'             
    test :camel_to_snake, 'camelCase',       'camel_case'      
    test :camel_to_snake, 'likeThis',        'like_this'       
    test :camel_to_snake, 'doTheEvolution',  'do_the_evolution'
    
    puts
    puts "Camel to Snake case"
    test :snake_to_camel, 'one',             'one'             
    test :snake_to_camel, 'camel_case',      'camelCase'      
    test :snake_to_camel, 'like_this',       'likeThis'       
    test :snake_to_camel, 'do_the_evolution', 'doTheEvolution'
      
    

    Outputs:

    
    Snake to Camel case
    one               : one               OK
    camelCase         : camel_case        OK
    likeThis          : like_this         OK
    doTheEvolution    : do_the_evolution  OK
    
    Camel to Snake case
    one               : one               OK
    camel_case        : camelCase         OK
    like_this         : likeThis          OK
    do_the_evolution  : doTheEvolution    OK
    
    

    @programmingpraxis fell free to delete my previous post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: