2012-07-06 8 views
8

Mam następujący kod, który działa, ale zastanawiam się, czy istnieje „groovier” sposób to zrobić:Czy istnieje jakiś sposób dodania kresek do łańcucha?

/** 
    * 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 
    } 
} 

Odpowiedz

2

Nie wiem czy mi się podoba to lepiej. Uczyniłbym też mapę pozycji statycznym finałem.

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('-') 
} 

Zakresy zarabiać na trochę mniej bałaganu, IMO:

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

z inject-z dwóch akumulatorów powinny być łatwe do zrobienia wersji list-of-kreska pozycjach też .

+0

można wyjaśnić * to rozstać? Co robi * w takim przypadku? – Gregg

+0

@Gregg Rozkłada tablicę tak, aby wyglądała jak dwa indywidualne parametry podciągu. Powinien jednak działać bez niego. Nie mogłem ci powiedzieć, dlaczego tak się stało, dlatego zostałem kurczakiem i zostawiłem go. –

8

Najpierw można użyć operatora łańcuchowego [], aby uzyskać ciągi znaków zamiast substring i usunąć zmienne pośrednie. Na przykład w przypadku dla length == 10:

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

Teraz jest nieco powtórzeń tam. można dostać zamiast najpierw uzyskać wszystkie segmenty isbn a następnie .join im '-':

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

A nawet dalej, zamiast o przedstawieniu isbn za każdym razem, można zrobić listę zakresów chcesz uzyskać, a następnie je wszystkie w tym samym czasie przy użyciu collect:

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

Jeśli idziesz do kodu gry w golfa, można również zrobić:

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

Zostawię to dla ciebie, aby dowiedzieć się, jak to działa, ale myślę, że to nie jest dobry pomysł, aby zostawić to na kod produkcyjny, chyba że chcesz zaskoczyć przyszłych hehe opiekunów.


Zauważ też, że format kiedy length == 13 jest taka sama jak dla length == 10 ale z innym prefiksem można następnie ponownie wykorzystać tę samą funkcję w tej sprawie. Cała funkcja (z kilku prób) byłoby:

/** 
* 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' 

Teraz, myślę, że istnieje kilka nieprzyjemnych zapachów w tym kodzie. Czy można isbn być null? Przynajmniej dla mnie nie wygląda to na funkcję, która musi zawracać sobie głowę nieważnością jej argumentu, a przynajmniej nie jest to jasne, czytając jej nazwę (powinno to być nazywane coś takiego jak formatIsbnOrNull, jeśli oba ciągi ISBN i wartości puste są akceptowane). Jeśli wartości puste są niepoprawne, pozwól mu wysadzić się z NullPointerException podczas uzyskiwania dostępu do isbn.length(), aby dzwoniący wiedział, że przekazał błędny argument, zamiast cichego zwracania tej samej wartości zerowej.

To samo dotyczy końcówki return ISBN. Czy oczekuje się, że funkcja otrzyma ciąg, który nie ma 10 ani 13 znaków? Jeśli nie, lepiej throw new IllegalArgumentException() i niech dzwoniący wie, że nazwała to błędnie.


Wreszcie, nie jestem pewien, czy jest to najbardziej "czytelne" rozwiązanie. Innym możliwym rozwiązaniem jest posiadanie ciągu znaków w formacie, np. '###-#-######-##-#', a następnie zastąpienie znaków # s znakami isbn. Myślę, że może to być bardziej samodokumentujące:

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

To wszystko świetnie. Dziękuję za szczegółową odpowiedź. Nie jestem pewien, z którym jeszcze się wybrać. Jeśli w końcu używam twojego, zmienię zaakceptowaną odpowiedź. – Gregg

+0

Myślę, że ten ostatni jest najlepszy, jeśli nieco nieprzejrzysty - ale IMO jest mało prawdopodobne, opiekun będzie dbał o wszystko poza wzorami, jeśli nawet to. Rozwiązanie asortymentowe jest takie samo; Nie mogę się zdecydować, który mi się podoba lepiej między wzorami/zakresami. –

+0

@Gregg Inną możliwością, o której tu nie wspomniałem, jest stworzenie ogólnej funkcji 'partition (str, sizes)', która pobiera ciąg znaków i zwraca listę pod-łańcuchów o zadanych rozmiarach (np. 'Partition ('hello', [ 4, 1]) == ['piekło', 'o'] '), więc otrzymanie numeru ISBN może być wyrażone jako" podziel pierwszy ciąg znaków na 1, 6, 2 i 1 znak i połącz go z myślnikami ":' partycja (isbn, [1, 6, 2, 1]). join ('-') '. To wymaga nieco więcej kodowania niż funkcja ogólna, ale implementacja 'formatIsbn' staje się bardzo klanem :) – epidemian

3

chciałbym spróbować użyć Regex ... Myślę, że to dość dużo czytelny, jeśli wiesz, jak używać regex, a to javascript inspirowane składni w Groovy jest całkiem fajny także .

Jeszcze jedno: jest całkiem jasne, patrząc na grupy przechwytywania, jak wygląda ciąg dla pożądanego formatowania.

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

Przy okazji, sugestia @epidemian przy użyciu odsyłaczy wstecz jest świetna! Myślę, że kod wyglądałby:

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

Bardzo podoba mi się wyrażenie regularne w tym przypadku; jest bardzo czytelny! Ale myślę, że część zamienna jest zbyt powtarzalna. Może mógłbyś użyć czegoś takiego jak '(isbn = ~/(\ d {1}) (\ d {6}) (\ d {2}) (\ d {1}) /) [0] .tail(). join ('-') 'lub' 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 $) 'jest bardzo sprytny: D – everton

+0

Bez referencji Naprawdę nie podoba mi się rozwiązanie regex, szczerze mówiąc - nawet z tym, że kod jest trudny do patrzenia z estetycznego punktu widzenia. –

4

Należy rozważyć dodanie metody do klasy String, jak pokazano tutaj. Zauważ, że ta odpowiedź jest sprytem na sprytnej sugestii w odpowiedzi epidemiologa (re: collect).

Uwaga:

Kod ten zwiększa String z asIsbn().

Zakres [0..2] nie wymaga połączenia z asIsbn(), ale symetria dwukrotnego użycia collect jest nieodparta.

Groovy zwraca ostatni wyraz w if/else, więc „powrót” nie jest konieczne

/** 
* 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 dla _typowości_ z [[..2.2, 3..12] .collect {delegate [it] .asIsbn()} '= D – epidemian

Powiązane problemy