2012-07-06 9 views
8

Tengo el siguiente código, que funciona, pero me pregunto si hay una manera de "groovy" de hacer esto:¿Hay una forma Groovier para agregar guiones a una cadena?

/** 
    * 10 digit - #-######-##-# 
    * 13 digit - ###-#-######-##-# 
    * */ 
private formatISBN(String isbn) { 
    if (isbn?.length() == 10) { 
    def part1 = isbn.substring(0, 1) 
    def part2 = isbn.substring(1, 7) 
    def part3 = isbn.substring(7, 9) 
    def part4 = isbn.substring(9, 10) 
    return "${part1}-${part2}-${part3}-${part4}" 
    } else if (isbn?.length() == 13) { 
    def part1 = isbn.substring(0, 3) 
    def part2 = isbn.substring(3, 4) 
    def part3 = isbn.substring(4, 10) 
    def part4 = isbn.substring(10, 12) 
    def part5 = isbn.substring(12, 13) 
    return "${part1}-${part2}-${part3}-${part4}-${part5}" 
    } else { 
    return isbn 
    } 
} 

Respuesta

2

No sé si me gusta esta mejor. Yo también haría del mapa de posición una estática final.

private isbnify(String isbn) { 
    def dashesAt = [ 10: [[0,1], [1,7], [7,9], [9,10]], 
        13: [[0,3], [3,4], [4,10], [10,12], [12,13]]] 
    def dashes = dashesAt[isbn?.length()] 
    (dashes == null) ? isbn 
        : dashes.collect { isbn.substring(*it) }.join('-') 
} 

rangos para hacer un poco menos desorden, la OMI:

private isbnify3(String isbn) { 
    def dashesAt = [ 10: [0, 1..6, 7..8, 9], 
        13: [0..2, 3, 4..9, 10..11, 12]] 
    def dashes = dashesAt[isbn?.length()] 
    dashes == null ? isbn : dashes.collect { isbn[it] }.join("-") 
} 

Con una inyección-con-dos-acumuladores que debería ser fácil de hacer una versión de lista de guión posiciones, también .

+0

Puede explicar el * se separe? ¿Qué hace * en ese caso? – Gregg

+0

@Gregg Desestructura la matriz para que se vea como los dos parámetros de subcadena individuales. Sin embargo, debería funcionar sin eso, creo. No podría decirte por qué lo haría, sin embargo, por eso era pollo y lo dejé. –

8

Primero podría usar el operador de cadena [] para obtener las subcadenas en lugar de substring y soltar las variables intermedias. Por ejemplo, en el caso de length == 10:

"${isbn[0]}-${isbn[1..6]}-${isbn[7..8]}-${isbn[9]}" 

Ahora, hay un poco de repetición allí. Usted puede obtener su lugar primero obtener todos los isbn segmentos y luego .join con '-':

[isbn[0], isbn[1..6], isbn[7..8], isbn[9]].join('-') 

Y, aún más, en lugar de hacer referencia a isbn cada vez, puede hacer una lista de los rangos que desea obtener y luego llegar a todos ellos al mismo tiempo utilizando collect:

[0, 1..6, 7..8, 9].collect { isbn[it] }.join('-') 

Si vas código para jugar al golf, también se puede hacer:

('-'+isbn)[1, 0, 2..7, 0, 8..9, 0, 10] 

Te dejaré que descubras cómo funciona eso, pero supongo que probablemente no sea una buena idea dejar eso en el código de producción, a menos que quieras sorprender a los futuros mantenedores jeje.


Además, observe que el formato cuando length == 13 es el mismo que para length == 10 pero con un prefijo diferente, a continuación, puede volver a utilizar la misma función que en ese caso. La función entera (con un par de pruebas) sería:

/** 
* 10 digit - #-######-##-# 
* 13 digit - ###-#-######-##-# 
**/ 
def formatIsbn(isbn) { 
    switch (isbn?.length()) { 
     case 10: return [0, 1..6, 7..8, 9].collect { isbn[it] }.join('-') 
     case 13: return isbn.take(3) + '-' + formatIsbn(isbn.drop(3)) 
     default: return isbn 
    } 
} 

assert formatIsbn('abcdefghij') == 'a-bcdefg-hi-j' 
assert formatIsbn('abcdefghijklm') == 'abc-d-efghij-kl-m' 

ahora, creo que hay algunos malos olores en ese código. ¿Puede isbn ser null? Al menos para mí, esto no parece una función que necesite preocuparse por la nulidad de su argumento, o al menos no está claro al leer su nombre (debería llamarse algo así como formatIsbnOrNull en vez de cadenas de ISBN y valores nulos) son aceptados). Si los valores nulos no son válidos, déjalo explotar con un NullPointerException al acceder al isbn.length() para que la persona que llama sepa que ha pasado un argumento incorrecto, en lugar de devolver silenciosamente el mismo valor nulo.

Lo mismo ocurre con el return ISBN al final. ¿Se espera que esa función reciba una cadena que no tiene 10 ni 13 caracteres de largo? De lo contrario, mejor throw new IllegalArgumentException() y avise a la persona que llama que lo han llamado incorrectamente.


Finalmente, no estoy seguro de si esta es la solución más "legible". Otra posible solución es tener una cadena para el formato, como '###-#-######-##-#' y luego reemplazar el # por los caracteres isbn. Creo que podría ser más auto-documentado:

def formatIsbn(isbn) { 
    def format = [ 
     10: '#-######-##-#', 
     13: '###-#-######-##-#' 
    ][isbn.length()] 
    def n = 0 
    format.replaceAll(/#/) { isbn[n++] } 
} 
+0

Esto es genial. Gracias por la respuesta detallada. No estoy seguro de a qué ir aún. Si termino usando el tuyo, cambiaré mi respuesta aceptada. – Gregg

+0

Creo que el último es el mejor, aunque un poco opaco, pero IMO es poco probable que un desarrollador se preocupe por nada excepto por los patrones, incluso eso. La solución de rangos es la misma; No puedo decidir cuál me gusta más entre patrones/rangos. –

+0

@Gregg Otra posibilidad que no mencioné aquí es hacer una función genérica 'partition (str, sizes)' que toma una cadena y devuelve una lista de subcadenas de tamaños dados (p. Ej. 'Partition ('hello', [ 4, 1]) == ['hell', 'o'] ') entonces obtener el ISBN se puede expresar como" particionar la cadena original tomando 1, 6, 2 y 1 caracteres y unirlos con guiones ":' partition (isbn, [1, 6, 2, 1]). join ('-') '. Se necesita un poco más de codificación que la función genérica, pero la implementación de 'formatIsbn' se vuelve muy clan :) – epidemian

3

me gustaría probar usando Regex ... Creo que es más o menos legible si sabe cómo utilizar expresiones regulares, y es javascript inspirado sintaxis en Groovy es bastante fresco también .

Una cosa más: es bastante claro, mirando a los grupos de captura, cómo se ve la secuencia para el formato deseado.

private formatISBN(String isbn) { 
    if (isbn?.length() == 10) { 
     m = isbn =~ /(\d{1})(\d{6})(\d{2})(\d{1})/ 
     return "${m[0][1]}-${m[0][2]}-${m[0][3]}-${m[0][4]}" 
    } else if (isbn?.length() == 13) { 
     m = isbn =~ /(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/ 
     return "${m[0][1]}-${m[0][2]}-${m[0][3]}-${m[0][4]}-${m[0][5]}"   
    } else { 
     return isbn 
    } 
} 

Btw, @epidemian suggestion using backreferences is great! Creo que el código se vería así:

private formatISBN(String isbn) { 
    if (isbn?.length() == 10) { 
     return isbn.replaceAll(/(\d{1})(\d{6})(\d{2})(\d{1})/, '$1-$2-$3-$4') 
    } else if (isbn?.length() == 13) { 
     return isbn.replaceAll(/(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/, '$1-$2-$3-$4-$5') 
    } else { 
     return isbn 
    } 
} 
+1

Me gusta mucho la expresión regular en este caso; es muy legible! Pero creo que la parte de reemplazo es demasiado repetitiva. Tal vez podrías usar algo como '(isbn = ~/(\ d {1}) (\ d {6}) (\ d {2}) (\ d {1}) /) [0] .tail(). join ('-') ', o' isbn.replaceAll (/ (\ d {1}) (\ d {6}) (\ d {2}) (\ d {1}) /, '$ 1- $ 2- $ 3- $ 4 ') '? = D – epidemian

+0

Whoa, 'isbn.replaceAll (/ (\ d {1}) (\ d {6}) (\ d {2}) (\ d {1}) /, '$ 1- $ 2- $ 3- $ 4') 'es muy, muy inteligente: D – everton

+0

Sin referencias anteriores Realmente no me gusta la solución de expresiones regulares, para ser sincero, incluso con, encuentro que el código es difícil de ver desde un punto de vista estético. –

4

Considere agregar el método a la clase String, como se muestra aquí. Tenga en cuenta que esta respuesta es un giro en una sugerencia inteligente en la respuesta de la epidemia (re: recoger).

Nota:

Este código aumenta cadena con asIsbn().

El rango [0..2] no necesita la llamada a asIsbn(), pero la simetría de usar collect dos veces es irresistible.

maravilloso devuelve la última expresión en if/else, por lo que 'retorno' no es necesario

/** 
* 10 digit - #-######-##-# 
* 13 digit - ###-#-######-##-# 
**/ 
String.metaClass.asIsbn = { -> 
    if (delegate.length() == 10) { 
     [0, 1..6, 7..8, 9].collect { delegate[it] }.join('-') 
    } else if (delegate.length() == 13) { 
     [0..2, 3..12].collect { delegate[it].asIsbn() }.join('-') 
    } else { 
     delegate 
    } 
} 

assert "abcdefghij".asIsbn() == 'a-bcdefg-hi-j' 
assert "abcdefghijklm".asIsbn() == 'abc-d-efghij-kl-m' 
assert "def".asIsbn() == "def" 
String s = null 
assert s?.asIsbn() == null 
+1

+1 para el _sexiness_ de '[0..2, 3..12] .collect {delegate [it] .asIsbn()}' = D – epidemian

Cuestiones relacionadas