Converter em camelCase

34

O desafio

Eu estava lendo o Java Style Guide do Google outro dia e me deparei com o algoritmo deles para converter qualquer string arbitrária em notação camelCase. Nesse desafio, você precisa implementar esse algoritmo, pois não deseja fazer tudo isso em sua mente quando estiver escrevendo seus envios Java super competitivos para os desafios do código-golfe.

Nota: Fiz alguns pequenos ajustes no algoritmo. Você precisa usar o especificado abaixo.

O algoritmo

Você começa com uma sequência de entrada arbitrária e aplica as seguintes operações a ela:

  1. Remova todos os apóstrofos `'
  2. Divida o resultado em palavras, dividindo em
    • caracteres que não são alfanuméricos e nem um dígito [^a-zA-Z0-9]
    • Letras maiúsculas cercadas por letras minúsculas nos dois lados. abcDefGhI jkpor exemplo, produzabc Def Ghi jk
  3. Minúsculas cada palavra.
  4. Coloque em maiúscula o primeiro caractere de todas, exceto a primeira palavra.
  5. Junte todas as palavras novamente.

Notas Adicionais

  • A entrada conterá apenas ASCII imprimível.
  • Se um dígito é a primeira letra de uma palavra, deixe-o como está e não capalize outra coisa nessa palavra.
  • A entrada sempre terá pelo menos um caractere.

Regras

  • Função ou programa completo permitido.
  • Regras padrão para entrada / saída.
  • Aplicam-se brechas padrão .
  • Isso é , portanto, a menor contagem de bytes vence. O desempate é uma submissão anterior.

Casos de teste

"Programação de quebra-cabeças e código de golfe" -> "programmingPuzzlesCodeGolf"
"Solicitação HTTP XML" -> "xmlHttpRequest"
"suporta IPv6 no iOS?" -> "suportaIpv6OnIos"
"SomeThing w1th, apo'strophe and 'punc] tuation" -> "someThingW1thApostrophesAndPuncTuation"
"nada de especial" -> "nada de especial"
"5pecial ca5e" -> "5pecialCa5e"
"1337" -> "1337"
"1337-spEAk" -> "1337Speak"
"o que é uma bagunça" -> "o que é que"
"abcD" -> "abcd"
"a" -> "a"
"B" -> "b"

Feliz codificação!

Denker
fonte
3
Interessante, eu nunca soube que isso se chamava "camelCase". Nome é justo eu suponho ...
Ashwin Gupta
4
Há mais: snake_case&PascalCase
Martijn
14
@ Martin snake_casepor causa de Python, é claro. FORTH também tem FORTHCASEe APL hasunreadable in any case
cat
O caso de teste 4 deve ter ApostropheSna saída.
Titus
@Titus Não, está correto. Os apóstrofos são removidos antes da entrada ser dividida.
Denker #

Respostas:

13

Retina , 56 bytes

A contagem de bytes assume a codificação ISO 8859-1.

T`'\`
S_`\W|_|(?<=[a-z])(?=[A-Z][a-z])
T`L`l
T`l`L`¶.
¶

Experimente online!

Explicação

Isso implementa a especificação literalmente:

T`'\`

Remova apóstrofos e backticks.

S_`\W|_|(?<=[a-z])(?=[A-Z][a-z])

Divida a string em caracteres que não sejam palavras (no regex, isso também exclui dígitos e sublinhados) ou sublinhados ou posições com letras minúsculas à esquerda e maiúsculas e minúsculas à direita. Isso criaria alguns segmentos vazios quando houver dois caracteres sem letra e sem dígito em uma linha ou mais importantes no início da string. Nós nos livramos daqueles com a _opção. Aqui, "divisão" significa colocar cada parte restante em sua própria linha.

T`L`l

Converta tudo para minúsculas.

T`l`L`¶.

Converta cada caractere que ocorre após o avanço de linha em maiúsculas. Isso ignora convenientemente a primeira palavra, porque não há avanço de linha na frente.

Livre-se dos feeds de linha para juntar tudo de novo.

Martin Ender
fonte
Você chegou antes de mim. Agradável!
mbomb007
Essa pergunta pode ser um pouco estranha, mas ... devo postar minha resposta se for mais curta que a sua e também na Retina? Eu estava trabalhando nisso antes que sua resposta aparecesse, mas funcionou e agora não sei se devo publicá-la.
Daavko
5
@daavko Claro, publique-o (normalmente decido com base em quão diferente é a abordagem da resposta existente ... se é exatamente a mesma coisa com um byte raspado em algum lugar, normalmente apenas comento essa resposta, mas se for muito mais curta de uma abordagem diferente, basta postar uma resposta separada).
Martin Ender
2
@daavko A busca é necessária. Observe que sua resposta não retém a capitalização, Thingembora deva.
Martin Ender
1
@ MartinBüttner Oh ... eu não percebi isso. Bem, vou responder com sucesso a outro desafio, então.
Daavko
11

Java, 198 190 bytes

+3 bytes porque esqueci que \W+== [^a-zA-Z0-9_]+e preciso corresponder[^a-zA-Z0-9]+

-11 bytes graças a user20093 - em ?:vez de if/else

Porque Java.

s->{String[]a=s.replaceAll("`|'","").split("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])");s="";for(String w:a){String t=w.toLowerCase();s+=a[0]==w?t:t.toUpperCase().charAt(0)+t.substring(1);}return s;}

Este é um lambda. Ligue assim:

UnaryOperator<String> op = s->{String[]a=s.replaceAll("`|'","").split("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])");s="";for(String w:a){String t=w.toLowerCase();s+=a[0]==w?t:t.toUpperCase().charAt(0)+t.substring(1);}return s;};
System.out.println(op.apply("Programming Puzzles & Code Golf"));

Versão legível:

public static String toCamelCase(String s) {
    String[] tokens = s
            .replaceAll("`|'", "") // 1. Remove all apostrophes
            .split("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])"); // 2. Split on [\W_]+ or between [a-z] and [A-Z][a-z]
    s = ""; // Reusing s for building output is cheap
    for (String token : tokens) {
        String lowercaseToken = token.toLowerCase(); // 3. Lowercase every word
        s += tokens[0].equals(token)?lowercaseToken:lowercaseToken.toUpperCase().charAt(0) + lowercaseToken.substring(1); // 4. Uppercase first char of all but first word
        // ^ 5. Join all words back together
    }
    return s;
}
CAD97
fonte
1
Não é Swift ...
CalculatorFeline #
2
Bem-vindo à programação de quebra-cabeças e código de golfe! Esta é uma boa primeira resposta!
Alex A.
1
@CatsAreFluffy What?
gato
? se você substituir instrução condicional (if / else) com expressão condicional (:) você poderia economizar cerca de 9 bytes
user902383
Não sei como eu perdi esse @ user902383 - adicionado por -11 bytes. Infelizmente, tive que adicionar 3 também para combinar _com um delimitador de token.
CAD97
10

JavaScript (ES6), 156 154 152 148 145 141 140 bytes

Obrigado @Neil (6 bytes), @ETHproductions (3 bytes) e @ edc65 (7 bytes)

a=>a[r='replace'](/`|'/g,a='')[r](/[a-z](?=[A-Z][a-z])/g,'$& ')[r](/[^\W_]+/g,b=>a+=(a?b[0].toUpperCase():'')+b.slice(!!a).toLowerCase())&&a

Remove apóstrofos, depois substitui para dividir em caracteres especiais / antes de maiúsculas circundadas e, em seguida, combina com o revestimento apropriado. Infelizmente, toLowerCase()e toUpperCase()são irritantemente longos e difíceis de evitar aqui ...

Mwr247
fonte
1
Eu estava trabalhando em uma abordagem diferente, na qual sua b.slice(i>0)abordagem explode, mas nesse meio tempo minha expressão de correspondência /[A-Z]?([a-z0-9]|[0-9A-Z]{2,})+([A-Z](?![a-z]))?/gparece economizar 2 bytes em relação à sua replaceabordagem engenhosa .
Neil
1
Ou eu poderia apenas salvar 2 bytes replacediretamente:replace(/[a-z](?=[A-Z][a-z])/g,'$& ')
Neil
1
Normalmente match...mappode ser substituído porreplace
edc65 2/16
1
@ edc65 Recebo no mínimo 160 bytes com essa abordagem:a=>a.replace(/`|'/g,'').replace(/[a-z](?=[A-Z][a-z])/g,'$& ').replace(/[\W_]*([a-z0-9]+)[\W_]*/gi,(_,b,i)=>(i?b[0].toUpperCase():'')+b.slice(i>0).toLowerCase())
ETHproductions
2
Por outro lado, gostaria de oferecer o b=>a+=(a?b[0].toUpperCase():'')+b.slice(!!a).toLowerCase()que acredito poupar mais 4 bytes.
Neil
7

vim, 69 68 66

:s/[`']//g<cr>:s/[a-z]\zs\ze[A-Z][a-z]\|\W\|_/\r/g<cr>o<esc>guggj<C-v>GgU:%s/\n<cr>

vim mais curto que o Perl ?! Que loucura é essa?

:s/[`']//g<cr>           remove ` and '
:s/                      match...
 [a-z]\zs\ze[A-Z][a-z]   right before a lowercase-surrounded uppercase letter
 \|\W\|_                 or a non-word char or underscore
 /\r/g<cr>               insert newlines between parts
o<esc>                   add an extra line at the end, necessary later...
gugg                     lowercasify everything
j                        go to line 2 (this is why we added the extra line)
<C-v>G                   visual select the first char of all-but-first line
gU                       uppercase
:%s/\n<cr>               join all lines into one

Agradecemos a Neil por detectar um pressionamento de tecla inútil!

Maçaneta da porta
fonte
Eu posso ver por que o último :stem um, %mas por que a inconsistência nos dois primeiros?
Neil
@ Neil Bah, memória muscular. Obrigado!
Maçaneta
5
Consegue ser menos legível que o Perl, também +1
cat
Estou totalmente adicionando isso ao meu .vimrc
moopet
1
@fruglemonkey 1. :%j<cr>é equivalente e mais curto. 2. Isso adiciona espaços entre as linhas.
Maçaneta
5

Mathematica 10.1, 101 bytes

""<>(ToCamelCase@{##2}~Prepend~ToLowerCase@#&@@StringCases[StringDelete[#,"`"|"'"],WordCharacter..])&

Usa o não documentado ToCamelCase, que funciona de maneira semelhante a, Capitalizemas define outros caracteres para minúsculas.

LegionMammal978
fonte
Não em 10.3.0 ..
A Simmons
Está ToCamelCase[n_,m_]:=n<>Capitalize/@mcorreto? Parece que sim. E por que usar Prependquando #~ToCamelCase~{##2}funciona?
CalculatorFeline
@CatsAreFluffy Isso me dá #ToCamelCase::argx: ToCamelCase called with 2 arguments; 1 argument is expected.
LegionMammal978
Bem, como o CamelCase funciona? Apenas ToCamelCase[n_]:=""<>Capitalize/@n?
CalculatorFeline
@CatsAreFluffy, veja isso .
LegionMammal978
5

Julia, 98 89 bytes

s->lcfirst(join(map(ucfirst,split(replace(s,r"['`]",""),r"[a-z]\K(?=[A-Z][a-z])|\W|_"))))

Esta é uma função anônima que aceita uma string e retorna uma string. Para chamá-lo, atribua-o a uma variável.

A abordagem aqui é a mesma que na maçaneta da porta resposta Perl porta : replaceapóstrofos e backticks com a string vazia, splitem uma matriz em uma expressão regular que corresponde aos casos necessários, mapa ucfirstfunção na matriz para colocar em maiúscula a primeira letra de cada elemento, joina matriz de volta em uma sequência e lcfirsto resultado para converter o primeiro caractere em minúscula.

Alex A.
fonte
Eu sempre gostei de Julia como um Python mais funcional e mais interessante, mas odeio a endsintaxe. Talvez eu vou usar apenas funções anônimas para tudo, então eu nunca digitar end: D
cat
4

Perl 67 + 1 = 68 bytes

y/'`//d;s/([a-z](?=[A-Z][a-z]))|\W|_/$1 /g;$_=lc;s/^ +| +(.)/\u$1/g

Requer a -pbandeira e-l para várias linhas:

$ perl -pl camelCase.pl input.txt
programmingPuzzlesCodeGolf
xmlHttpRequest
supportsIpv6OnIos:
someThingW1thApostrophesAndPuncTuation
nothingSpecial
5pecialCa5e
1337
1337Speak
abcd

Como funciona:

y/'`//d;                            # Remove ' and `
s/([a-z](?=[A-Z][a-z]))|\W|_/$1 /g; # Replace according to '2. Split...' this will create
                                    #   a space separated string.
$_=lc;                              # lower case string
s/^ +| +(.)/\u$1/g                  # CamelCase the space separated string and remove any
                                    #   potential leading spaces.
andlrc
fonte
2

Perl, 87 80 78 bytes

y/'`//d;$_=join'',map{ucfirst lc}split/[a-z]\K(?=[A-Z][a-z])|\W|_/,$_;lcfirst

Byte adicionado para o -psinalizador.

Primeiro, usamos o y/// operador de transliteração para dexcluir todos os '`caracteres da entrada:

y/'`//d;

Depois vem a carne do código:

                         split/[a-z]\K(?=[A-Z][a-z])|\W|_/,$_;

(divida a string de entrada $_nos locais apropriados, usando o sofisticado\K na string de correspondência para excluir a parte que a precede da correspondência real)

          map{ucfirst lc}

(mapeie cada parte dividida da string e coloque a string inteira em minúscula e, em seguida, o primeiro caractere da string modificada)

$_=join'',

(junte uma string vazia e atribua novamente ao sublinhado mágico $_, que é impresso no final)

Por fim, minúscula a primeira letra , correspondendo-a ao regex e usando \lna cadeia de substituição com uma incorporada, economizando 2 bytes em relação ao método anterior:

lcfirst

Obrigado a @ MartinBüttner por 7 bytes ( [^a-zA-Z\d]-> \W|_)!

Maçaneta da porta
fonte
1
Como eu invejo que \K...;)
Martin Ender
2

Lua, 127 bytes

t=''l=t.lower z=io.read()for x in z:gmatch('%w+')do t=t..(t==''and l(x:sub(1,1))or x:sub(1,1):upper())..l(x:sub(2))end return t

Aceita uma sequência de caracteres de stdin e retorna resultados camelizados.

Provavelmente ainda procurará uma solução melhor, pois armazenar tudo em uma variável parece ineficiente.

Mas de qualquer maneira, bastante simples em geral:

 z:gmatch('%w+')

Essa é a beleza que me salvou um pouco de bytes. O gmatch dividirá a string com base no padrão: %w+que captura apenas alfanuméricos.

Depois disso, são simples operações de string. string.upper, string.lower e done.

Skyl3r
fonte
2

PHP, 145 122 133 bytes

<?=join(split(" ",lcfirst(ucwords(strtolower(preg_replace(["#`|'#","#\W|_#","#([a-z])([A-Z][a-z])#"],[""," ","$1 $2"],$argv[1]))))));

Salve no arquivo, ligue da CLI.
Recebe entrada de um único argumento de linha de comando; evite aspas e espaços em branco quando necessário.

demolir

<?=                 // 9. print result
join(split(" ",     // 8. remove spaces
    lcfirst(        // 7. lowercase first character
    ucwords(        // 6. uppercase first character in every word
    strtolower(     // 5. lowercase everything
    preg_replace(
        ["#`|'#",   "#\W|_#",   "#([a-z])([A-Z][a-z])#"],
        ["",        " ",        "$1 $2"],
        // 2. replace apostrophes with empty string (remove them)
                    // 3. replace non-word characters with space
                                // 4. insert space before solitude uppercase
        $argv[1]    // 1. take input from command line
    ))))
));

lcfirstpermitido reduzir isso para um único comando, economizando 23 bytes.
A correção dos apóstrofos custa 11 bytes para o caso de substituição adicional.

Titus
fonte
1

Kotlin , 160 bytes

fun a(s: String)=s.replace(Regex("['`]"),"").split(Regex("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])")).map{it.toLowerCase().capitalize()}.joinToString("").decapitalize()

Meu objetivo era ser o Scala, o outro "Java alternativo", então estou um pouco feliz com meus resultados. Eu roubei o regex da resposta Java .

Teste com:

fun main(args: Array<String>) {
    val testCases = arrayOf(
            "Programming Puzzles & Code Golf",
            "XML HTTP request",
            "supports IPv6 on iOS?",
            "SomeThing w1th, apo'strophe's and' punc]tuation",
            "nothing special",
            "5pecial ca5e",
            "1337",
            "1337-spEAk",
            "abcD",
            "a",
            "B")
    testCases.forEach { println(a(it)) }

}
Nathan Merrill
fonte
Neste ponto, acho que todo mundo está "emprestando" o regex otimizado \W|_|(?<=[a-z])(?=[A-Z][a-z])ou modificando-o levemente, por exemplo. [\W_]+
precisa
você pode salvar alguns no mapa e função de extensãofun String.a()=replace(Regex("['`]"),"").split(Regex("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])")).joinToString(""){it.toLowerCase().capitalize()}.decapitalize()
poss 18/01
1

Scala, 181 170 144

def f(s:String)={val l=s.replaceAll("'|`","")split("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])")map(_.toLowerCase);l(0)+l.tail.map(_.capitalize).mkString}

Testador:

val testCases = List(
  "Programming Puzzles & Code Golf" -> "programmingPuzzlesCodeGolf",
  "XML HTTP request" -> "xmlHttpRequest"
  // etc
)
println(testCases.map(t=>if(t._2!=f(t._1))s"FAIL:${f(t._1)}"else"PASS").mkString("\n"))

Adereços para CAD97 e desculpas a Nathan Merrill :)

contente
fonte
1
Você pode salvar 6 bytes substituindo [^a-zA-Z0-9]+por [\\W_]+.
CAD97
0

Caracteres C 272

O programa C passa a string para camelCase entre aspas como argumento 1. Há muitas dicas nesta declaração de problema ...

#define S strlen(t)
#define A isalnum(t[i])
j=0;main(i,v)char**v;{char*p=v[1],*t;char o[99]={0};while(t=strtok(p," [{(~!@#$%^*-+=)}]")){i=0;p+=S+1;while((!A)&&i<S)i++;if(i!=S){o[j]=((j++==0)?tolower(t[i++]):toupper(t[i++]));while(i<S){if(A)o[j++]=t[i];i++;}}}puts(o);}
cleblanc
fonte
Você precisa #include<string.h> de strlen, strtoke toupper, e #include<ctype.h>para isalnum.
Mego
Eu não precisava dele usando o gcc 3.4.4 no cygwin. Eles devem ser vinculados automaticamente, assumindo int externo.
Cleblanc # 04/16
Com o ./camel "Programming Puzzles & Code Golf"cygwin (compilado com o gcc 3.4.4), recebo programmingPuzzlesCodeEGolf. Mesma saída com 5.3.0.
Mego
Porcaria. eu também. Eu devo ter criado um bug enquanto jogava golfe. Eu estou olhando agora ...
cleblanc
O problema foi que eu adicionei as outras strings do tokenizer depois do golfe e não o testei o suficiente. Se você remover o '&' da chamada strtok, ele funcionará nessa entrada.
Cleblanc 4/16
0

JavaScript, 123 bytes

v=>v[r="replace"](/[`']/g,"")[r](/^.|.$|[A-Z][^a-z]+/g,x=>x.toLowerCase())[r](/[^a-z0-9]+./ig,x=>x.slice(-1).toUpperCase())

Versão legível

v=>
  v.replace(/[`']/g,"")
  .replace(/^.|.$|[A-Z][^a-z]+/g,x=>x.toLowerCase())
  .replace(/[^a-z0-9]+./ig,x=>x.slice(-1).toUpperCase())

Remova os apóstrofos, coloque o primeiro caractere em minúscula, o último caractere em minúscula e qualquer agrupamento de vários caracteres maiúsculos, corresponda a qualquer grupo de 1 ou mais caracteres não alfanuméricos + 1 outro caractere, substitua pelo último caractere em maiúscula.

[r = "replace"] truque da solução Mrw247.

Grax32
fonte