2012-07-06 46 views
8

我有下面的代码,它的工作原理,但我不知道是否有这样做的“更巧妙”的方式:是否有一种更加方便的方法将字符串添加到字符串中?

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

回答

2

说不上来,如果我喜欢这个更好。我也会让位置图成为一个静态的决赛。

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

范围做了少一点混乱,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("-") 
} 

随着注入与 - 两个蓄能器应该很容易做到列表的划线位版本,太。

+0

你能解释一下*一部分呢? *在这种情况下做了什么? – Gregg 2012-07-06 01:43:36

+0

@Gregg它解构数组,使其看起来像两个单独的子串参数。不过,我认为,它应该没有它。我不能告诉你为什么会这样,但是,这就是为什么我是鸡肉,并把它留在里面。 – 2012-07-06 01:47:05

8

您可以首先使用[]字符串运算符来获取子字符串而不是substring并删除中间变量。例如在length == 10的情况下:

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

现在,这里有一些重复。你可以得到,而不是先得到所有的isbn段,然后.join他们'-'

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

而且,更进一步,而不是每次都引用isbn,你可以让你想要得到,然后范围的列表让他们都使用同一时间collect

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

如果你打算代码高尔夫,你也可以这样做:

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

我会留给你弄清楚它是如何工作的,但我想这可能不是一个好主意,将它留在生产代码上,除非你想让未来的维护者惊喜。


另外,还要注意的是,当length == 13是相同length == 10但有不同的前缀,格式,你可以然后再用在这种情况下相同的功能。全功能(有一对夫妇的测试)将是:

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

现在,我认为有在代码中的一些不好的气味。可以isbnnull?至少对我而言,这看起来不像是一个需要对其参数的无效性进行打扰的函数,或者至少通过读取它的名称并不清楚(它应该被称为类似formatIsbnOrNull而不是如果ISBN字符串和空值被接受)。如果空值无效,那么在访问isbn.length()时让它冒充NullPointerException,以便调用者知道他们传递了错误的参数,而不是静默地返回相同的空值。

最后的return ISBN也是如此。预计该函数将接收一个既不是10个字符也不是13个字符的字符串?如果不是,更好的throw new IllegalArgumentException(),让来电者知道他们错误地称呼它。


最后,我不确定这是否是最“可读”的解决方案。另一种可能的解决方案是使用格式的字符串,如'###-#-######-##-#',然后用isbn字符替换#。我想可能是更多的自我记录:

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

这是非常棒的。感谢您的详细解答。我不知道该走哪条路。如果我最终使用你的话,我会改变我接受的答案。 – Gregg 2012-07-06 03:26:57

+0

我认为最后一个是最好的,如果有点不透明 - 但IMO不太可能维护者会关心除了模式之外的任何东西,即使是这样。范围解决方案是相同的;我无法决定哪种模式/范围更适合我。 – 2012-07-06 03:32:58

+0

@Gregg另一个我没有提到的可能是制作一个通用函数'partition(str,sizes)',它接受一个字符串并返回给定大小的子字符串列表(例如'partition('hello',[ 4,1])== ['hell','o']')所以然后得到的ISBN可以表示为“分割原始字符串取1,6,2和1个字符,并用破折号连接它们:”分区(isbn,[1,6,2,1])。join(' - ')'。它需要更多的编码,泛型函数,但'formatIsbn'的实现变得非常氏族:) – epidemian 2012-07-06 04:10:44

3

我会尝试使用Regex ......我认为这是非常可读,如果你知道如何使用正则表达式,它是在常规的JavaScript启发语法是很酷也。

还有一件事:它非常清楚,看着捕获组,你的字符串看起来像所需的格式。

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

顺便说一句,@epidemian建议使用反向引用是伟大的!我认为代码将如下所示:

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

我真的很喜欢这种情况下的正则表达式;它非常可读!但我认为替换部分太重复了。也许你可以使用'(isbn =〜/(\ d {1})(\ d {6})(\ d {2})(\ d {1})/)[0] .tail()。 (\ d {1})或\ isbn.replaceAll(/(\ d {1})(\ d {6})(\ d {1})/,'$ 1- $ 2- $ 3- $ 4' )'? = D – epidemian 2012-07-06 04:24:09

+0

Whoa,'isbn.replaceAll(/(\ d {1})(\ d {6})(\ d {2})(\ d {1})/,'$ 1- $ 2- $ 3- $ 4' )'是非常非常聪明的:D – everton 2012-07-06 13:03:46

+0

没有后向引用,我真的不喜欢正则表达式的解决方案,说实话 - 即使是,我发现从审美的角度来看代码很难看。 – 2012-07-07 01:39:16

4

考虑将方法添加到String类,如下所示。请注意,这个答案是在流行病的答案中的一个聪明的建议的旋转(re:collect)。

注:

此代码增强与asIsbn字符串()。

范围[0..2]不需要调用asIsbn(),但两次使用collect的对称性是不可阻止的。

的Groovy返回if/else最后一个表达式,所以“回归”是没有必要的

/** 
* 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 [0..2,3..12] .collect {delegate [it] .asIsbn()} = D的_sexiness_ +1 – epidemian 2012-07-06 04:25:11

相关问题