exercism

Exercism - Twelve Days

This post shows you how to get Twelve Days exercise of Exercism.

Stevinator Stevinator
9 min read
SHARE
exercism dart flutter twelve-days

Preparation

Before we click on our next exercise, let’s see what concepts of DART we need to consider

Twelve Days Exercise

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:

  1. Store the data: Create lists for ordinals (first, second, third, etc.) and gifts (the items given each day)
  2. 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
  3. 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:

  1. 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)
  2. 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
  3. String recite(int start, int end) - Main method that generates verses:

    • Uses a list comprehension to build verses for days from start to end
    • Calls _buildVerse(day) for each day in the range
    • Joins all verses with newlines (\n)
  4. 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 - 1 down to 1 (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
Stevinator

Stevinator

Stevinator is a software engineer passionate about clean code and best practices. Loves sharing knowledge with the developer community.