Exercism - Nucleotide Count
This post shows you how to get Nucleotide Count 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.
Maps
A Map is a collection of key-value pairs. Each key in a map is unique, and you can use keys to look up their corresponding values. Maps can store any type of data as keys and values.
void main() {
// Map with String keys and int values
Map<String, int> counts = {
'A': 3,
'C': 1,
'G': 1,
'T': 2,
};
// Accessing values
print(counts['A']); // 3
// Setting values
counts['A'] = 5;
print(counts); // {A: 5, C: 1, G: 1, T: 2}
// Creating empty map
Map<String, int> empty = {};
empty['A'] = 0;
print(empty); // {A: 0}
}
String Indexing and Character Access
Strings in Dart can be accessed by index to get individual characters. You can iterate through a string character by character.
void main() {
String dna = "GATTACA";
// Access character by index
print(dna[0]); // 'G'
print(dna[1]); // 'A'
// Get string length
print(dna.length); // 7
// Iterate through characters
for (int i = 0; i < dna.length; i++) {
print(dna[i]);
}
}
Switch Statements
Switch statements allow you to check a value against multiple cases and execute different code for each case. They’re perfect for handling multiple possible values.
void main() {
String nucleotide = 'A';
switch (nucleotide) {
case 'A':
print("Adenine");
break;
case 'C':
print("Cytosine");
break;
case 'G':
print("Guanine");
break;
case 'T':
print("Thymine");
break;
default:
print("Invalid");
}
}
Exceptions
Exceptions are used to handle error conditions in Dart. You can throw exceptions when something goes wrong, and catch them to handle the error gracefully.
void main() {
// Throwing a basic exception
if (someCondition) {
throw Exception('Something went wrong');
}
// Catching exceptions
try {
riskyOperation();
} catch (e) {
print('Error: $e');
}
}
Custom Exceptions
You can create custom exceptions by implementing the Exception interface. This allows you to create specific error types for your application.
class InvalidNucleotideException implements Exception {
final String message;
InvalidNucleotideException(this.message);
@override
String toString() => message;
}
void main() {
// Throw custom exception
throw InvalidNucleotideException('Invalid nucleotide: X');
}
For Loops
For loops allow you to iterate through a collection or a range of numbers. They’re essential for processing each character in a string.
void main() {
String text = "ABC";
// Iterate by index
for (int i = 0; i < text.length; i++) {
print(text[i]);
}
// Iterate through characters directly
for (var char in text.split('')) {
print(char);
}
}
Increment Operators
The increment operator (++) increases a variable by 1. It’s commonly used for counting occurrences.
void main() {
int count = 0;
// Increment
count++;
print(count); // 1
// Pre-increment vs post-increment
int a = 5;
int b = a++; // b = 5, a = 6
int c = ++a; // c = 7, a = 7
}
Introduction
Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. All known life depends on DNA!
Note: You do not need to understand anything about nucleotides or DNA to complete this exercise.
DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important! We call the order of these nucleotides in a bit of DNA a “DNA sequence”.
We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as “ATTACG” for a DNA sequence of 6 nucleotides. ‘A’ for adenine, ‘C’ for cytosine, ‘G’ for guanine, and ‘T’ for thymine.
Given a string representing a DNA sequence, count how many of each nucleotide is present. If the string contains characters that aren’t A, C, G, or T then it is invalid and you should signal an error.
Examples
- “GATTACA” → ‘A’: 3, ‘C’: 1, ‘G’: 1, ‘T’: 2
- “INVALID” → error
What are nucleotides?
Nucleotides are organic molecules that serve as the building blocks of DNA and RNA. The four nucleotides in DNA are:
- Adenine (A) - Pairs with thymine
- Cytosine (C) - Pairs with guanine
- Guanine (G) - Pairs with cytosine
- Thymine (T) - Pairs with adenine
The sequence of these nucleotides encodes genetic information.
— Biology
How can we count nucleotides?
To count nucleotides in a DNA sequence:
- Initialize counters for each nucleotide (A, C, G, T) to 0
- Iterate through each character in the DNA string
- For each character:
- If it’s ‘A’, increment the A counter
- If it’s ‘C’, increment the C counter
- If it’s ‘G’, increment the G counter
- If it’s ‘T’, increment the T counter
- If it’s none of these, throw an exception
- Return a map with the counts for each nucleotide
For example, with “GATTACA”:
- G → G counter = 1
- A → A counter = 1
- T → T counter = 1
- T → T counter = 2
- A → A counter = 2
- C → C counter = 1
- A → A counter = 3
- Result: {‘A’: 3, ‘C’: 1, ‘G’: 1, ‘T’: 2}
Solution
class InvalidNucleotideException implements Exception {
final String message;
InvalidNucleotideException(this.message);
@override
String toString() => message;
}
class NucleotideCount {
Map<String, int> count(String strand) {
int a = 0, c = 0, g = 0, t = 0;
for (int i = 0; i < strand.length; i++) {
switch (strand[i]) {
case 'A': a++; break;
case 'C': c++; break;
case 'G': g++; break;
case 'T': t++; break;
default:
throw InvalidNucleotideException('Invalid nucleotide detected: ${strand[i]}');
}
}
return {'A': a, 'C': c, 'G': g, 'T': t};
}
}
Let’s break down the solution:
-
InvalidNucleotideException- A custom exception class:- Implements the
Exceptioninterface - Stores an error message
- Overrides
toString()to return the message when the exception is converted to a string
- Implements the
-
NucleotideCountclass - Contains the counting logic -
count(String strand)- Counts nucleotides in the DNA strand:- Initializes counters:
int a = 0, c = 0, g = 0, t = 0 - Iterates through each character using a for loop:
for (int i = 0; i < strand.length; i++) - Uses a switch statement to handle each possible nucleotide:
case 'A': a++; break;- Increments A counter and exits switchcase 'C': c++; break;- Increments C counter and exits switchcase 'G': g++; break;- Increments G counter and exits switchcase 'T': t++; break;- Increments T counter and exits switchdefault:- Handles any invalid character by throwingInvalidNucleotideExceptionwith a descriptive message
- Returns a map with the counts:
{'A': a, 'C': c, 'G': g, 'T': t}
- Initializes counters:
The solution efficiently counts nucleotides using a switch statement for clear, readable code, and properly handles invalid input by throwing a custom exception.
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