Convertir a camelCase

34

El reto

Estaba leyendo la Guía de estilo Java de Google el otro día y me topé con su algoritmo para convertir cualquier cadena arbitraria en notación camelCase. En este desafío, debe implementar este algoritmo ya que no desea hacer todo esto en su cabeza cuando está escribiendo sus presentaciones Java súper competitivas para desafíos de código de golf.

Nota: Hice algunos pequeños ajustes al algoritmo. Debe usar el que se especifica a continuación.

El algoritmo

Comienza con una cadena de entrada arbitraria y le aplica las siguientes operaciones:

  1. Eliminar todos los apóstrofes `'
  2. Divide el resultado en palabras dividiéndolo en
    • caracteres que no son alfanuméricos ni un dígito [^a-zA-Z0-9]
    • Letras mayúsculas que están rodeadas por letras minúsculas en ambos lados. abcDefGhI jkpor ejemplo rendimientosabc Def Ghi jk
  3. Minúsculas cada palabra.
  4. En mayúscula el primer carácter de todas menos la primera palabra.
  5. Une todas las palabras de nuevo juntas.

Notas adicionales

  • La entrada solo contendrá ASCII imprimible.
  • Si un dígito es la primera letra de una palabra, déjelo como está y no escriba otra cosa en esta palabra.
  • La entrada siempre tendrá al menos un carácter.

Reglas

Casos de prueba

"Programación de puzzles y código de golf" -> "programaciónPuzzlesCodeGolf"
"Solicitud XML HTTP" -> "xmlHttpRequest"
"¿Soporta IPv6 en iOS?" -> "supportsIpv6OnIos"
"SomeThing w1th, apo'strophe's y 'punc] tuation" -> "someThingW1thApostrophesAndPuncTuation"
"nada especial" -> "nada especial"
"5pecial ca5e" -> "5pecialCa5e"
"1337" -> "1337"
"1337-spEAk" -> "1337Speak"
"whatA mess" -> "whataMess"
"abcD" -> "abcd"
"a" -> "a"
"B" -> "b"

¡Feliz codificación!

Denker
fuente
3
Interesante, nunca supe que esto se llamaba "camelCase". El nombre es apropiado, supongo ...
Ashwin Gupta
44
Hay más: snake_case&PascalCase
Martijn
14
@Martijn snake_casepor Python, por supuesto. FORTH también tiene FORTHCASEy APL tieneunreadable in any case
cat
El caso de prueba 4 debería tener ApostropheSen la salida.
Titus
@Titus No, es correcto. Los apóstrofos se eliminan antes de dividir la entrada.
Denker

Respuestas:

13

Retina , 56 bytes

El recuento de bytes asume la codificación ISO 8859-1.

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

Pruébalo en línea!

Explicación

Esto implementa la especificación literalmente:

T`'\`

Elimine los apóstrofes y las comillas.

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

Divida la cadena en caracteres que no sean palabras (en expresiones regulares, esto también excluye dígitos y guiones bajos), o guiones bajos o posiciones que tienen una letra minúscula a la izquierda y mayúsculas, minúsculas a la derecha. Esto crearía algunos segmentos vacíos cuando hay dos caracteres sin letras, sin dígitos en una fila, o más importantes al comienzo de la cadena. Nos deshacemos de aquellos con la _opción. Aquí, "dividir" significa poner cada parte restante en su propia línea.

T`L`l

Convierta todo a minúsculas.

T`l`L`¶.

Convierta cada carácter que ocurre después del salto de línea a mayúsculas. Esto omitirá convenientemente la primera palabra porque no hay salto de línea delante de ella.

Deshágase de los avances de línea para unir todo nuevamente.

Martin Ender
fuente
Me ganaste a eso. ¡Buena esa!
mbomb007
Esta pregunta puede ser un poco extraña, pero ... ¿debo publicar mi respuesta si es más corta que la tuya y también en Retina? Estaba trabajando en ello antes de que apareciera su respuesta, pero luego lo hizo y ahora no sé si debería publicarlo.
daavko
55
@daavko Claro, publíquelo (generalmente decido en función de cuán diferente es el enfoque de la respuesta existente ... si es exactamente lo mismo con un byte recortado en algún lugar, normalmente solo comento esa respuesta, pero si es mucho más corta de un enfoque diferente, entonces simplemente publicaría una respuesta por separado).
Martin Ender
2
@daavko Sin embargo, la búsqueda es necesaria. Tenga en cuenta que su respuesta no conserva la capitalización de Thingaunque debería.
Martin Ender
1
@ MartinBüttner Oh ... no me di cuenta de eso. Oh, bueno, entonces responderé con éxito algún otro desafío.
daavko
11

Java, 198 190 bytes

+3 bytes porque olvidé que \W+== [^a-zA-Z0-9_]+y necesito hacer coincidir[^a-zA-Z0-9]+

-11 bytes gracias a user20093 - en ?:lugar 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;}

Esta es una lambda. Llame así:

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

Versión legible:

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
fuente
1
No es rápido ...
CalculatorFeline
2
¡Bienvenido a Programming Puzzles & Code Golf! Esta es una buena primera respuesta!
Alex A.
1
@CatsAreFluffy ¿Qué?
gato
si reemplaza la declaración condicional (if / else) con la expresión condicional (? :), podría ahorrar alrededor de 9 bytes
user902383
No sé cómo me perdí ese @ user902383, agregado por -11 bytes. Desafortunadamente, tuve que agregar 3 también para que coincidiera _como un delimitador de token.
CAD97
10

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

Gracias @Neil (6 bytes), @ETHproductions (3 bytes) y @ 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

Elimina los apóstrofos, luego reemplaza para dividir en caracteres especiales / antes de mayúsculas rodeadas, luego se combina con la carcasa adecuada. Desafortunadamente, toLowerCase()y toUpperCase()son molestamente largos y difíciles de evitar aquí ...

Mwr247
fuente
1
Estaba trabajando en un enfoque diferente que su b.slice(i>0)enfoque elimina del agua, pero mientras tanto, la expresión regular de mi partido /[A-Z]?([a-z0-9]|[0-9A-Z]{2,})+([A-Z](?![a-z]))?/gparece ahorrar 2 bytes sobre su replaceenfoque ingenioso .
Neil
1
O podría simplemente guardar 2 bytes en su replacedirectamente:replace(/[a-z](?=[A-Z][a-z])/g,'$& ')
Neil
1
Por match...maplo general, se puede reemplazar conreplace
edc65
1
@ edc65 obtengo un mínimo de 160 bytes con ese enfoque: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 otro lado, me gustaría ofrecer b=>a+=(a?b[0].toUpperCase():'')+b.slice(!!a).toLowerCase()lo que creo que le ahorra otros 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>

¿Es más corto que Perl? ¿Qué es esta locura?

: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

¡Gracias a Neil por detectar una pulsación inútil!

Pomo de la puerta
fuente
Puedo ver por qué el último :stiene un %pero ¿por qué la inconsistencia en los dos primeros?
Neil
@Neil Bah, memoria muscular. ¡Gracias!
Pomo de la puerta
55
Se las arregla para ser menos legible que Perl, también +1
gato
Estoy agregando esto por completo a mi .vimrc
moopet
1
@fruglemonkey 1. :%j<cr>es equivalente y más corto. 2. Eso agrega espacios entre líneas.
Pomo de la puerta
5

Mathematica 10.1, 101 bytes

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

Utiliza el indocumentado ToCamelCase, que funciona de manera similar Capitalizepero establece otros caracteres en minúsculas.

LegionMammal978
fuente
No en 10.3.0 ..
A Simmons
Es ToCamelCase[n_,m_]:=n<>Capitalize/@mcorrecto? Parece que. ¿Y por qué usarlo Prependcuando #~ToCamelCase~{##2}funciona?
CalculatorFeline
@CatsAreFluffy Eso me daToCamelCase::argx: ToCamelCase called with 2 arguments; 1 argument is expected.
LegionMammal978
Bueno, ¿cómo funciona CamelCase? Solo ToCamelCase[n_]:=""<>Capitalize/@n?
CalculatorFeline
@CatsAreFluffy, mira esto .
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 es una función anónima que acepta una cadena y devuelve una cadena. Para llamarlo, asígnelo a una variable.

El enfoque aquí es el mismo que en la respuesta de Perl de Doorknob : replaceapóstrofos y retrocesos con la cadena vacía, spliten una matriz en una expresión regular que coincide con los casos necesarios, mapla ucfirstfunción sobre la matriz para poner en mayúscula la primera letra de cada elemento, joinla matriz hacia atrás en una cadena, y lcfirstel resultado para convertir el primer carácter a minúsculas.

Alex A.
fuente
Siempre me ha gustado Julia como una Python más funcional e interesante, pero odio la endsintaxis. Tal vez solo usaré funciones anónimas para todo, luego nunca tendré que escribir 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

Requiere la -pbandera, y -lpara líneas múltiples:

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

Cómo 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
fuente
2

Perl, 87 80 78 bytes

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

Byte agregado para la -pbandera.

Primero, usamos el y///operador de transliteración para delegir todos los '`caracteres en la entrada:

y/'`//d;

Luego viene la carne del código:

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

(divida la cadena de entrada $_en las ubicaciones apropiadas, utilizando la fantasía \Ken la cadena de coincidencia para excluir la porción que la precede de la coincidencia real)

          map{ucfirst lc}

(mapee sobre cada porción dividida de la cadena y ponga la cadena completa en minúsculas, luego ponga el primer carácter de la cadena modificada en mayúsculas)

$_=join'',

(unirse en una cadena vacía y reasignar al guión bajo mágico $_, que se imprime al final)

Finalmente, ponemos en minúscula la primera letra regexándola y utilizándola \len la cadena de reemplazo con una función incorporada, ahorrando 2 bytes sobre el método anterior:

lcfirst

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

Pomo de la puerta
fuente
1
Cómo envidio eso \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

Acepta una cadena de stdin y devuelve resultados camelizados.

Probablemente todavía busque una mejor solución ya que almacenar todo en una variable se siente ineficiente.

Pero de todos modos, bastante simple en general:

 z:gmatch('%w+')

Esta es la belleza que me ahorró un poco de bytes. gmatch dividirá la cadena en función del patrón: %w+que solo toma caracteres alfanuméricos.

Después de eso son simples operaciones de cadena. string.upper, string.lower y listo.

Skyl3r
fuente
2

PHP, 145 122 133 bytes

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

Guardar en archivo, llamar desde CLI.
Toma información de un argumento de línea de comando único; escape comillas y espacios en blanco donde sea necesario.

Descompostura

<?=                 // 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 reducir esto a un solo comando, ahorrando 23 bytes.
La fijación de los apóstrofos cuesta 11 bytes para el caso de reemplazo adicional.

Titus
fuente
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()

Mi objetivo era ser Scala, la otra "Java alternativa", así que estoy algo contento con mis resultados. Robé la expresión regular de la respuesta de Java .

Pruébalo con:

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
fuente
En este punto, creo que todo el mundo está "tomando prestado" la expresión regular optimizada \W|_|(?<=[a-z])(?=[A-Z][a-z])o modificándola ligeramente, por ejemplo. [\W_]+
CAD97
puede guardar algunos en la función de mapa y extensiónfun String.a()=replace(Regex("['`]"),"").split(Regex("[\\W_]+|(?<=[a-z])(?=[A-Z][a-z])")).joinToString(""){it.toLowerCase().capitalize()}.decapitalize()
Pos
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}

Ensayador:

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

Atrezzo a CAD97 y disculpas a Nathan Merrill :)

claro
fuente
1
Puede guardar 6 bytes reemplazando [^a-zA-Z0-9]+con [\\W_]+.
CAD97
0

C 272 caracteres

El programa C pasa la cadena a camelCase entre comillas como argumento 1. Hay muchas trampas en esta declaración del 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
fuente
You need to #include<string.h> for strlen, strtok, and toupper, and #include<ctype.h> for isalnum.
Mego
I didn't need it using gcc 3.4.4 in cygwin. They must be automatically linked in, assuming extern int.
cleblanc
With ./camel "Programming Puzzles & Code Golf" on cygwin (compiled with gcc 3.4.4), I get programmingPuzzlesCodeEGolf. Same output with 5.3.0.
Mego
Crap. me too. I must've created a bug while golfing it. I'm looking at it now...
cleblanc
The problem was I added the other tokenizer strings after golfing and didn't test it well enough. If you remove the '&' from the strtok call it works on that input.
cleblanc
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())

Readable version

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

Remove the apostrophes, make the first character lower case, the last character lowercase, and any grouping of multiple uppercase characters, match any group of 1 or more non-alphanumeric chars + 1 other character, replace with that last character capitalized.

[r="replace"] trick from Mrw247's solution.

Grax32
fuente