Exercism - Twelve Days
This post shows you how to get Twelve Days 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.
Static Constants
Static constants are class-level constants that belong to the class itself rather than to instances. They’re defined using static const and are shared across all instances of the class.
class Song {
static const List<String> ordinals = [
'first', 'second', 'third', 'fourth'
];
static const List<String> items = [
'a Partridge in a Pear Tree.',
'two Turtle Doves,'
];
void printOrdinal(int index) {
print(ordinals[index]); // Access static constant
}
}
void main() {
// Access static constant without creating an instance
print(Song.ordinals[0]); // 'first'
Song song = Song();
song.printOrdinal(1); // 'second'
}
Lists
Lists are ordered collections of items. You can access elements by index, iterate over them, and perform various operations.
void main() {
List<String> gifts = [
'a Partridge in a Pear Tree.',
'two Turtle Doves,',
'three French Hens,'
];
// Access by index
print(gifts[0]); // 'a Partridge in a Pear Tree.'
print(gifts[1]); // 'two Turtle Doves,'
// Access with calculation
int day = 3;
print(gifts[day - 1]); // 'three French Hens,'
}
List Comprehensions with For Loops
List comprehensions can use traditional for loops with increment or decrement. This allows you to iterate through ranges and build lists conditionally.
void main() {
// List comprehension with increment
List<int> numbers = [for (int i = 1; i <= 5; i++) i];
print(numbers); // [1, 2, 3, 4, 5]
// List comprehension with decrement
List<int> reversed = [for (int i = 5; i > 0; i--) i];
print(reversed); // [5, 4, 3, 2, 1]
// Building a list in reverse order
List<String> items = ['a', 'b', 'c', 'd'];
List<String> reversedItems = [
for (int i = items.length - 1; i >= 0; i--) items[i]
];
print(reversedItems); // [d, c, b, a]
}
String Interpolation
String interpolation allows you to embed expressions and variables directly within strings using ${expression} or $variable.
void main() {
String ordinal = 'first';
String gift = 'a Partridge in a Pear Tree.';
// Basic interpolation
String verse = 'On the $ordinal day of Christmas my true love gave to me: $gift';
print(verse);
// Expression interpolation
int day = 3;
String intro = 'On the ${day}rd day of Christmas';
print(intro);
// Complex interpolation
List<String> gifts = ['two Turtle Doves,', 'a Partridge in a Pear Tree.'];
String line = 'On the first day: ${gifts.join(' ')}';
print(line);
}
String Joining
The join() method combines all elements of a list into a single string, with an optional separator between elements.
void main() {
List<String> gifts = [
'two Turtle Doves,',
'and a Partridge in a Pear Tree.'
];
// Join with space
String verse = gifts.join(' ');
print(verse); // "two Turtle Doves, and a Partridge in a Pear Tree."
// Join with newline
List<String> verses = ['Verse 1', 'Verse 2', 'Verse 3'];
String song = verses.join('\n');
print(song);
// Verse 1
// Verse 2
// Verse 3
}
Conditional Logic
Conditional statements allow you to execute different code based on conditions. This is essential for handling special cases.
void main() {
int day = 1;
// Simple if statement
if (day == 1) {
print('First day - special handling');
} else {
print('Other days - normal handling');
}
// Conditional expression
String result = day == 1 ? 'single gift' : 'multiple gifts';
print(result);
// Early return pattern
String buildVerse(int day) {
if (day == 1) {
return 'On the first day: a Partridge in a Pear Tree.';
}
return 'On day $day: multiple gifts';
}
print(buildVerse(1)); // Special case
print(buildVerse(2)); // Normal case
}
Helper Methods
Helper methods (often private methods starting with _) encapsulate reusable logic, making code more organized and maintainable.
class Song {
// Public method
String recite(int start, int end) {
return [for (int day = start; day <= end; day++) _buildVerse(day)].join('\n');
}
// Private helper method
String _buildVerse(int day) {
// Implementation details
return 'Verse $day';
}
}
For Loops with Decrement
For loops can count backwards by using decrement (i--). This is useful when you need to process items in reverse order.
void main() {
List<String> gifts = [
'a Partridge in a Pear Tree.',
'two Turtle Doves,',
'three French Hens,'
];
// Iterate backwards
for (int i = gifts.length - 1; i >= 0; i--) {
print(gifts[i]);
}
// three French Hens,
// two Turtle Doves,
// a Partridge in a Pear Tree.
// Build list in reverse order
List<String> reversed = [
for (int i = gifts.length - 1; i > 0; i--) gifts[i]
];
print(reversed); // ['three French Hens,', 'two Turtle Doves,']
}
Introduction
Your task in this exercise is to write code that returns the lyrics of the song: “The Twelve Days of Christmas.”
“The Twelve Days of Christmas” is a common English Christmas carol. Each subsequent verse of the song builds on the previous verse.
Instructions
The lyrics your code returns should exactly match the full song text shown below.
Lyrics
Each verse follows a pattern:
- First verse: “On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree.”
- Subsequent verses: Add new gifts in reverse order, ending with “and a Partridge in a Pear Tree.”
For example:
- Day 1: “a Partridge in a Pear Tree.”
- Day 2: “two Turtle Doves, and a Partridge in a Pear Tree.”
- Day 3: “three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.”
Each verse builds upon the previous one by adding a new gift at the beginning.
What is “The Twelve Days of Christmas”?
“The Twelve Days of Christmas” is an English Christmas carol that enumerates a series of increasingly grand gifts given on each of the twelve days of Christmas. The song is cumulative, meaning each verse repeats all the previous gifts and adds a new one. The gifts range from “a Partridge in a Pear Tree” on the first day to “twelve Drummers Drumming” on the twelfth day.
— Wikipedia
How can we generate the lyrics?
To generate the lyrics:
- Store the data: Create lists for ordinals (first, second, third, etc.) and gifts (the items given each day)
- Build each verse: For each day, construct the verse by:
- Using the ordinal for that day
- Collecting all gifts from that day down to day 1 in reverse order
- Handling the special case for day 1 (no “and” before the partridge)
- Adding “and” before the partridge for all other days
- Join verses: Combine multiple verses with newlines if a range is requested
The key insight is that each verse includes all previous gifts in reverse order, with the newest gift first and the partridge last (with “and” for days > 1).
Solution
class TwelveDays {
static const _ordinals = [
'first', 'second', 'third', 'fourth', 'fifth', 'sixth',
'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth'
];
static const _gifts = [
'a Partridge in a Pear Tree.',
'two Turtle Doves,',
'three French Hens,',
'four Calling Birds,',
'five Gold Rings,',
'six Geese-a-Laying,',
'seven Swans-a-Swimming,',
'eight Maids-a-Milking,',
'nine Ladies Dancing,',
'ten Lords-a-Leaping,',
'eleven Pipers Piping,',
'twelve Drummers Drumming,'
];
String recite(int start, int end) =>
[for (int day = start; day <= end; day++) _buildVerse(day)].join('\n');
String _buildVerse(int day) {
final intro = 'On the ${_ordinals[day - 1]} day of Christmas my true love gave to me:';
if (day == 1) return '$intro ${_gifts[0]}';
final gifts = [
for (int i = day - 1; i > 0; i--) _gifts[i],
'and ${_gifts[0]}'
];
return '$intro ${gifts.join(' ')}';
}
}
Let’s break down the solution:
-
static const _ordinals- Stores the ordinal numbers as strings:- Contains all twelve ordinals: ‘first’, ‘second’, ‘third’, etc.
- Accessed by index:
_ordinals[day - 1](day 1 → index 0)
-
static const _gifts- Stores all the gifts in order:- Index 0: ‘a Partridge in a Pear Tree.’ (the base gift)
- Index 1-11: The other gifts in order
- Each gift is a complete phrase with proper punctuation
-
String recite(int start, int end)- Main method that generates verses:- Uses a list comprehension to build verses for days from
starttoend - Calls
_buildVerse(day)for each day in the range - Joins all verses with newlines (
\n)
- Uses a list comprehension to build verses for days from
-
String _buildVerse(int day)- Helper method that builds a single verse:- Introduction: Creates the opening line using the ordinal for that day
- Special case (day == 1): Returns early with just the partridge (no “and”)
- Normal case (day > 1):
- Builds a list of gifts in reverse order using a list comprehension
- Iterates from
day - 1down to1(decrementing:i--) - Adds “and” before the partridge for proper grammar
- Joins all gifts with spaces
The solution elegantly handles the cumulative nature of the song by building each verse with all previous gifts in reverse order, ensuring the newest gift appears first and the partridge appears last (with “and” for days > 1).
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