Есть ли более простой способ добавить тире в строку?
У меня есть следующий код, который работает, но мне интересно, есть ли способ" groovier " сделать это:
/**
* 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
}
}
4 ответов
Не знаю, нравится ли мне это больше. Я бы сделал карту положения статическим финалом.
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("-")
}
С впрыскивать-с-2-аккумуляторами должно быть легко сделать версию список -- черточк-положений, слишком.
можно использовать []
строковый оператор, чтобы получить подстроки вместо 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'
теперь, я думаю, что в этом коде есть плохие запахи. Может isbn
быть null
? По крайней мере, для меня это не похоже на функцию, которая должна беспокоиться о ничтожности своего аргумента, или, по крайней мере, это не ясно, читая его имя (его следует называть чем-то вроде formatIsbnOrNull
вместо этого, если принимаются как строки ISBN, так и значения null). Если значения null недопустимы, пусть он взорвется с NullPointerException
при обращении isbn.length()
таким образом, вызывающий знает, что они передали неправильный аргумент, вместо того, чтобы молча возвращать тот же null.
то же самое return ISBN
в конце. Ожидается ли, что эта функция получит строку длиной не 10 и не 13 символов? Если нет, лучше throw new IllegalArgumentException()
и пусть звонящий знает, что они назвали это неправильно.
наконец, я не уверен, что это самое "читаемое" решение. Другим возможным решением является наличие строки для формата, например '###-#-######-##-#'
а затем заменить #
С isbn
символы. Я думаю, что это может быть более самодокументированный:
def formatIsbn(isbn) {
def format = [
10: '#-######-##-#',
13: '###-#-######-##-#'
][isbn.length()]
def n = 0
format.replaceAll(/#/) { isbn[n++] }
}
попробуйте добавить метод в класс String, как показано здесь. Обратите внимание, что этот ответ является спином на умное предложение в ответе epidemian (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
Я бы попробовал использовать Regex
... Я думаю, что это довольно читабельно, если вы знаете, как использовать регулярное выражение, и это синтаксис, вдохновленный javascript в groovy, тоже довольно круто.
еще одна вещь: довольно ясно, глядя на группы захвата, как выглядит ваша строка для желаемого форматирования.
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 предложение с использованием backreferences отлично! Я думаю, что код будет выглядеть так:
private formatISBN(String isbn) {
if (isbn?.length() == 10) {
return isbn.replaceAll(/(\d{1})(\d{6})(\d{2})(\d{1})/, '---')
} else if (isbn?.length() == 13) {
return isbn.replaceAll(/(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/, '----')
} else {
return isbn
}
}