Exercism - Luhn
This post shows you how to get Luhn exercise of Exercism.
Preparation
Before we click on our next exercise, let’s see what concepts of DART we need to consider

So we need to use the following concepts.
String ReplaceAll Method
The replaceAll() method replaces all occurrences of a pattern in a string with another string. It’s useful for removing or replacing characters.
void main() {
String cardNumber = "4539 3195 0343 6467";
// Remove all spaces
String cleaned = cardNumber.replaceAll(' ', '');
print(cleaned); // "4539319503436467"
// Remove other characters
String text = "4539-3195-0343-6467";
String noDashes = text.replaceAll('-', '');
print(noDashes); // "4539319503436467"
}
String Length Property
The length property returns the number of characters in a string. It’s useful for validation.
void main() {
String number = "4539319503436467";
// Check length
print(number.length); // 16
// Validate minimum length
if (number.length <= 1) {
print('Invalid: too short');
}
// Check if valid length
bool isValidLength = number.length > 1;
print(isValidLength); // true
}
Regular Expressions (RegExp)
Regular expressions allow you to match patterns in strings. The hasMatch() method checks if a string matches a pattern.
void main() {
// Pattern: only digits
RegExp pattern = RegExp(r'^\d+$');
// Valid patterns
print(pattern.hasMatch('4539319503436467')); // true
print(pattern.hasMatch('123')); // true
// Invalid patterns
print(pattern.hasMatch('4539 3195')); // false (contains space)
print(pattern.hasMatch('4539-3195')); // false (contains dash)
print(pattern.hasMatch('abc123')); // false (contains letters)
// Pattern breakdown:
// ^ - start of string
// \d+ - one or more digits
// $ - end of string
}
For Loops
For loops allow you to iterate a specific number of times. They’re perfect for processing each character in a string.
void main() {
String number = "4539319503436467";
// Iterate through each position
for (int i = 0; i < number.length; i++) {
print('Position $i: ${number[i]}');
}
// Process from right to left
for (int i = 0; i < number.length; i++) {
int rightIndex = number.length - 1 - i;
print('Right to left position $i: ${number[rightIndex]}');
}
}
String Indexing
You can access individual characters in a string using square brackets with an index. The first character is at index 0.
void main() {
String number = "4539";
// Access by index
print(number[0]); // '4'
print(number[1]); // '5'
print(number[2]); // '3'
print(number[3]); // '9'
// Access from right
int len = number.length;
print(number[len - 1]); // '9' (last character)
print(number[len - 2]); // '3' (second from right)
// Access from right using calculation
for (int i = 0; i < number.length; i++) {
int rightIndex = number.length - 1 - i;
print(number[rightIndex]); // 9, 3, 5, 4
}
}
Integer Parsing
The int.parse() method converts a string to an integer. It’s needed when processing string characters as numbers.
void main() {
String digit = "5";
// Parse to integer
int num = int.parse(digit);
print(num); // 5
// Parse character from string
String number = "4539";
int firstDigit = int.parse(number[0]);
print(firstDigit); // 4
// Process each digit
for (int i = 0; i < number.length; i++) {
int digit = int.parse(number[i]);
print(digit);
}
}
isOdd Property
The isOdd property returns true if an integer is odd, and false if it’s even. It’s useful for identifying every second element when counting from 0.
void main() {
// Check if index is odd
print(0.isOdd); // false (even)
print(1.isOdd); // true (odd)
print(2.isOdd); // false (even)
print(3.isOdd); // true (odd)
// Use to identify every second element
for (int i = 0; i < 10; i++) {
if (i.isOdd) {
print('Index $i is odd - process this');
}
}
// Output: Index 1, 3, 5, 7, 9 are odd
}
Modulo Operator
The modulo operator (%) returns the remainder of a division operation. It’s used to check if a number is evenly divisible by another.
void main() {
int sum = 80;
// Check if divisible by 10
int remainder = sum % 10;
print(remainder); // 0 (80 is divisible by 10)
// Validate Luhn
bool isValid = sum % 10 == 0;
print(isValid); // true
// Another example
int sum2 = 36;
bool isValid2 = sum2 % 10 == 0;
print(isValid2); // false (36 is not divisible by 10)
}
Conditional (Ternary) Operator
The ternary operator (? :) provides a concise way to write if-else statements. It’s useful for simple conditional assignments.
void main() {
int digit = 7;
// Double the digit
int doubled = digit * 2;
print(doubled); // 14
// If > 9, subtract 9; otherwise use doubled value
int result = doubled > 9 ? doubled - 9 : doubled;
print(result); // 5 (14 - 9 = 5)
// One-liner
int processed = digit * 2 > 9 ? digit * 2 - 9 : digit * 2;
print(processed); // 5
// Equivalent to:
if (digit * 2 > 9) {
processed = digit * 2 - 9;
} else {
processed = digit * 2;
}
}
Introduction
At the Global Verification Authority, you’ve just been entrusted with a critical assignment. Across the city, from online purchases to secure logins, countless operations rely on the accuracy of numerical identifiers like credit card numbers, bank account numbers, transaction codes, and tracking IDs. The Luhn algorithm is a simple checksum formula used to help identify mistyped numbers.
A batch of identifiers has just arrived on your desk. All of them must pass the Luhn test to ensure they’re legitimate. If any fail, they’ll be flagged as invalid, preventing mistakes such as incorrect transactions or failed account verifications.
Can you ensure this is done right? The integrity of many services depends on you.
Instructions
Determine whether a number is valid according to the Luhn formula.
The number will be provided as a string.
Validating a Number
Strings of length 1 or less are not valid. Spaces are allowed in the input, but they should be stripped before checking. All other non-digit characters are disallowed.
Example 1: Valid Credit Card Number
The number to be checked is 4539 3195 0343 6467.
The first step of the Luhn algorithm is to start at the end of the number and double every second digit, beginning with the second digit from the right and moving left.
4539 3195 0343 6467
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (double these)
If the result of doubling a digit is greater than 9, we subtract 9 from that result. We end up with:
8569 6195 0383 3437
Finally, we sum all digits. If the sum is evenly divisible by 10, the original number is valid.
8 + 5 + 6 + 9 + 6 + 1 + 9 + 5 + 0 + 3 + 8 + 3 + 3 + 4 + 3 + 7 = 80
80 is evenly divisible by 10, so number 4539 3195 0343 6467 is valid!
Example 2: Invalid Canadian SIN
The number to be checked is 066 123 478.
We start at the end of the number and double every second digit, beginning with the second digit from the right and moving left.
066 123 478
↑ ↑ ↑ ↑ (double these)
If the result of doubling a digit is greater than 9, we subtract 9 from that result. We end up with:
036 226 458
We sum the digits:
0 + 3 + 6 + 2 + 2 + 6 + 4 + 5 + 8 = 36
36 is not evenly divisible by 10, so number 066 123 478 is not valid!
What is the Luhn algorithm?
The Luhn algorithm, also known as the “modulus 10” algorithm, is a simple checksum formula used to validate a variety of identification numbers, including credit card numbers, IMEI numbers, and Canadian Social Insurance Numbers. It was created by IBM scientist Hans Peter Luhn and described in U.S. Patent No. 2,950,048, filed on January 6, 1954, and granted on August 23, 1960.
— Wikipedia
How can we validate using the Luhn algorithm?
To validate a number using the Luhn algorithm:
- Clean the input: Remove spaces (spaces are allowed but should be removed)
- Validate format: Check that length > 1 and contains only digits
- Process from right to left:
- Start at the rightmost digit
- For every second digit (odd indices when counting from right), double it
- If doubling results in > 9, subtract 9
- Sum all digits: Add up all the processed digits
- Check validity: If the sum is divisible by 10, the number is valid
The key insight is processing digits from right to left and doubling every second digit, with special handling for digits that become > 9 after doubling.
For example, with “4539 3195 0343 6467”:
- Remove spaces: “4539319503436467”
- Process from right (indices 0, 1, 2, 3… from right):
- Index 0 (rightmost): 7 → 7
- Index 1: 6 → 6 × 2 = 12 → 12 - 9 = 3
- Index 2: 4 → 4
- Index 3: 3 → 3 × 2 = 6
- … and so on
- Sum: 80
- 80 % 10 == 0 → Valid!
Solution
class Luhn {
bool valid(String value) {
final d = value.replaceAll(' ', '');
if (d.length <= 1 || !RegExp(r'^\d+$').hasMatch(d)) return false;
int sum = 0;
for (int i = 0; i < d.length; i++) {
int n = int.parse(d[d.length - 1 - i]);
if (i.isOdd) n = n * 2 > 9 ? n * 2 - 9 : n * 2;
sum += n;
}
return sum % 10 == 0;
}
}
Let’s break down the solution:
-
bool valid(String value)- Main method that validates a number:- Takes a string (may contain spaces) as input
- Returns
trueif valid according to Luhn,falseotherwise
-
final d = value.replaceAll(' ', '')- Remove spaces:- Removes all spaces from the input string
- Spaces are allowed but must be removed before processing
- Example: “4539 3195 0343 6467” → “4539319503436467”
-
if (d.length <= 1 || !RegExp(r'^\d+$').hasMatch(d)) return false- Validate format:- Length check:
d.length <= 1- Strings of length 1 or less are invalid - Pattern check:
!RegExp(r'^\d+$').hasMatch(d)- Must contain only digits^\d+$means: start to end, one or more digits
- Returns
falseimmediately if validation fails
- Length check:
-
int sum = 0- Initialize sum:- Will accumulate the sum of all processed digits
-
for (int i = 0; i < d.length; i++)- Loop through all positions:irepresents the position from the right (0 = rightmost, 1 = second from right, etc.)- Iterates through all digits in the number
-
int n = int.parse(d[d.length - 1 - i])- Get digit from right:d.length - 1 - i: Calculates the index from the left- When i=0: gets rightmost digit (index length-1)
- When i=1: gets second from right (index length-2)
- And so on
int.parse(...): Converts the character to an integer- Example: For “4539”, when i=0: gets ‘9’ (rightmost)
-
if (i.isOdd) n = n * 2 > 9 ? n * 2 - 9 : n * 2- Double every second digit:i.isOdd: Checks if position from right is odd (1, 3, 5, …)- These are the positions that should be doubled (second, fourth, sixth from right)
n * 2 > 9 ? n * 2 - 9 : n * 2: Ternary operator- If doubling results in > 9: subtract 9
- Otherwise: use the doubled value
- Example: If n=6 and i=1 (odd): 6 × 2 = 12 > 9 → 12 - 9 = 3
-
sum += n- Add to sum:- Accumulates the processed digit value
- After the loop, sum contains the total of all processed digits
-
return sum % 10 == 0- Check validity:- If sum modulo 10 equals 0, the number is valid
- Returns
truefor valid numbers,falseotherwise
The solution efficiently implements the Luhn algorithm by processing digits from right to left, doubling every second digit, and checking if the sum is divisible by 10.
A video tutorial for this exercise is coming soon! In the meantime, check out my YouTube channel for more Dart and Flutter tutorials. 😉
Visit My YouTube Channel