patternkotlinMinor
Credit card / IMEI check digit in Kotlin
Viewed 0 times
creditcardimeikotlindigitcheck
Problem
I recently had the need to implement Luhn's Algorithm in a Java/Kotlin application. I needed a function that would add the check digit to the string of a number like a credit card or IMEI. It could be easily modified to check if the last digit is valid.
At first, I had a code of around 20 lines in Java, but by using every Kotlin quirk I could think of, I managed to do it in one line.
Here it is in a more understandable fashion
I understand that obfuscating code like this in a shared code environment would be very bad, but still I think it's great that we can get to do this and that Kotlin avoids so much boilerplate. What do you think about using one-line functions like this one?
At first, I had a code of around 20 lines in Java, but by using every Kotlin quirk I could think of, I managed to do it in one line.
fun String.addLastImeiDigit(): String = "$this${10 - this.withIndex().map { if (it.index.and(1) == 0) it.value.toInt() - 48 else (2 * (it.value.toInt() - 48)).run { (this % 10) + (this / 10) } }.sum() % 10}"Here it is in a more understandable fashion
fun String.addLastImeiDigit(): String {
return "$this${10 - this.withIndex().map {
if (it.index.and(1) == 0) /* if even */
it.value.toInt() - 48 /* ascii to Int */
else
(2 * (it.value.toInt() - 48)).run {
(this % 10) + (this / 10) /* sum both digits */
}
}.sum() % 10}"
}I understand that obfuscating code like this in a shared code environment would be very bad, but still I think it's great that we can get to do this and that Kotlin avoids so much boilerplate. What do you think about using one-line functions like this one?
Solution
I personally like one-line functions as long as they are short but I also prefer breaking things out into smaller functions and/or objects to improve readability, reuse, etc. e.g.:
With the following also defined:
Strings.kt
Numbers.kt
NumericalChars.kt
IntMath.kt
fun String.plusLuhnCheckDigit() = this + luhnCheckDigit(this)
fun luhnCheckDigit(digits: CharSequence): Char {
return digits.sumByIndexed { index, digit ->
when {
index.isEven -> digit.asInt()
else -> sumDigits(2 * digit.asInt())
}
}.let { sum -> 10 - sum % 10 }.asChar()
}With the following also defined:
Strings.kt
inline fun CharSequence.sumByIndexed(selector: (Int, Char) -> Int): Int {
return foldIndexed(0) { index, sum, element -> sum + selector(index, element) }
}Numbers.kt
val Int.isEven: Boolean
get() = and(1) == 0NumericalChars.kt
fun Char.asInt() = toInt() - 48
fun Int.asChar() = toChar() + 48IntMath.kt
val Int.magnitude: Int
get() = if (this < 0) -this else this
fun sumDigits(n: Int): Int = if (n.magnitude < 10) n else n % 10 + sumDigits(n / 10)Code Snippets
fun String.plusLuhnCheckDigit() = this + luhnCheckDigit(this)
fun luhnCheckDigit(digits: CharSequence): Char {
return digits.sumByIndexed { index, digit ->
when {
index.isEven -> digit.asInt()
else -> sumDigits(2 * digit.asInt())
}
}.let { sum -> 10 - sum % 10 }.asChar()
}inline fun CharSequence.sumByIndexed(selector: (Int, Char) -> Int): Int {
return foldIndexed(0) { index, sum, element -> sum + selector(index, element) }
}val Int.isEven: Boolean
get() = and(1) == 0fun Char.asInt() = toInt() - 48
fun Int.asChar() = toChar() + 48val Int.magnitude: Int
get() = if (this < 0) -this else this
fun sumDigits(n: Int): Int = if (n.magnitude < 10) n else n % 10 + sumDigits(n / 10)Context
StackExchange Code Review Q#139636, answer score: 3
Revisions (0)
No revisions yet.