Categories
Begin Python

The 12 Days of Christmas… or any countdown song

Loops are a big part of programming, because one of the benefits of programs is that they can automate repetitive tasks for humans. If we were to type out the complete lyrics to “The 12 Days of Christmas,” we would end up having to do a lot of typing! And it wouldn’t be fun typing—it would be boring, repetitive typing.

What about if you were to tell someone the lyrics to “The 12 Days of Christmas”? How would you describe it without repeating yourself? That’s pretty much how this program works.

So if I were to describe in English how to sing the song, I would say “There are 12 days. Each day has its own set of gifts for that day. You start with the gifts for the 1st day. Then you go to the 2nd and say the gifts for the 2nd day and the 1st day. Then you keep going to the 3rd day and 4th day all the way until the 12th day, when you count down all the gifts until you get back down to the 1st day.” Even that would be a bit confusing. Fortunately, there are easier ways to give the instructions in Python.

You may remember dictionaries from the rock, paper, scissors game. We’re going to use a different type of dictionary for this, though, because normal Python dictionaries don’t stay in order, and the order is critical for the 12 Days of Christmas.

I’m going to give you a bunch of code to type in (yes, as always better to retype than copy/paste if you can), but I’m also going to explain along the way what’s happening:

# We need to import OrderedDict from the collections module, because dictionaries in Python usually aren't ordered
from collections import OrderedDict

# Create the ordered dictionary that pairs a day of Christmas with the set of gifts for that day
days_gifts=OrderedDict([("1st" , "A partridge in a Pear Tree"),
                        ("2nd" , "Two Turtle Doves"),
                        ("3rd" , "Three French Hens"),
                        ("4th" , "Four Calling Birds"),
                        ("5th" , "Five Golden Rings"),
                        ("6th" , "Six Geese a Laying"),
                        ("7th" , "Seven Swans a Swimming"),
                        ("8th" , "Eight Maids a Milking"),
                        ("9th" , "Nine Ladies Dancing"),
                        ("10th" , "Ten Lords a Leaping"),
                        ("11th" , "Eleven Pipers Piping"),
                        ("12th", "12 Drummers Drumming")])

We could use a for loop to go through the dictionary, but since we have to know at all times where we are (for example, if you’re on the 4th day, you have to know that 3rd is the next day, etc.), we’ll keep a counter to know exactly what part of the dictionary we’re in.

We’re going to use a variable called counter that will store what number we’re on. Remember that the first item in the list is item number 0, not item number 1. Let’s just do a simple loop to go through each of the days from the 1st day to the 12th day:

# We need to import OrderedDict from the collections module, because dictionaries in Python usually aren't ordered
from collections import OrderedDict

# Create the ordered dictionary that pairs a day of Christmas with the set of gifts for that day
days_gifts=OrderedDict([("1st" , "A partridge in a Pear Tree"),
                        ("2nd" , "Two Turtle Doves"),
                        ("3rd" , "Three French Hens"),
                        ("4th" , "Four Calling Birds"),
                        ("5th" , "Five Golden Rings"),
                        ("6th" , "Six Geese a Laying"),
                        ("7th" , "Seven Swans a Swimming"),
                        ("8th" , "Eight Maids a Milking"),
                        ("9th" , "Nine Ladies Dancing"),
                        ("10th" , "Ten Lords a Leaping"),
                        ("11th" , "Eleven Pipers Piping"),
                        ("12th", "12 Drummers Drumming")])

# Initialize a counter starting at beginning of the list
counter=0

# Loop through each of the days until we get to 0 from 11
while counter < len(days_gifts):
 
   # Get the actual day from the ordered dictionary
   current_day=days_gifts.keys()[counter]
 
   # Print the intro to the day
   print "On the %s day of Christmas, my true love gave to me:" % (current_day)

   # Print the gift given
   print "%s," % days_gifts.values()[counter]
 
   # Increase first counter to get closer to the end of the list (the 12th day of Christmas)
   counter+=1

If you run that and all goes well, you should see 12 days of Christmas and what was given on each day.

But that’s just a start. That’s not how the song works. You get way more presents than that!

# We need to import OrderedDict from the collections module, because dictionaries in Python usually aren't ordered
from collections import OrderedDict
 
# Create the ordered dictionary that pairs a day of Christmas with the set of gifts for that day
days_gifts=OrderedDict([("1st" , "A partridge in a Pear Tree"),
                        ("2nd" , "Two Turtle Doves"),
                        ("3rd" , "Three French Hens"),
                        ("4th" , "Four Calling Birds"),
                        ("5th" , "Five Golden Rings"),
                        ("6th" , "Six Geese a Laying"),
                        ("7th" , "Seven Swans a Swimming"),
                        ("8th" , "Eight Maids a Milking"),
                        ("9th" , "Nine Ladies Dancing"),
                        ("10th" , "Ten Lords a Leaping"),
                        ("11th" , "Eleven Pipers Piping"),
                        ("12th", "12 Drummers Drumming")])

# Initialize a counter starting at beginning of the list
counter=0

# Loop through each of the days until we get to 0 from 11
while counter < len(days_gifts):
 
   # Get the actual day from the ordered dictionary
   current_day=days_gifts.keys()[counter]
 
   # Print the intro to the day
   print "On the %s day of Christmas, my true love gave to me:" % (current_day)
 
   # Set up a second counter, because we do a loop within a loop until we get to the partridge in a pear tree.
   # For now, we'll make the second counter the same as the first
   second_counter=counter

   # The second counter is going to move in the other direction.
   # While the first counter is going from the beginning of the dictionary to the end of the dictionary,
   # the second counter is going from the current place to the beginning of the dictionary.
   while second_counter >= 0:

      # Print what gifts are for the second counter
      print "%s," % days_gifts.values()[second_counter]
 
      # Decrease the second counter so we get a little closer to the beginning of the list (the 1st day of Christmas)
      second_counter-=1
      
   # Increase first counter to get closer to the end of the list (the 12th day of Christmas)
   counter+=1

Okay. Why do we need a second counter? Well, think about the 4th day of Christmas, as an example. For the 4th day, we need to keep track of being on the 4th day (so the next time we sing the song, we’ll know to go to the 5th day)—that’s the regular counter variable, but then we also need to count down to the 3rd day, the 2nd day, and then the 1st day—that would be the second_counter.

Notice a problem when you run this? There is supposed to be an “And” in there if it’s not the first day to end with the partridge in a pear tree. There also is a comma at the end of the last gift every time.

# We need to import OrderedDict from the collections module, because dictionaries in Python usually aren't ordered
from collections import OrderedDict
 
# Create the ordered dictionary that pairs a day of Christmas with the set of gifts for that day
days_gifts=OrderedDict([("1st" , "A partridge in a Pear Tree"),
                        ("2nd" , "Two Turtle Doves"),
                        ("3rd" , "Three French Hens"),
                        ("4th" , "Four Calling Birds"),
                        ("5th" , "Five Golden Rings"),
                        ("6th" , "Six Geese a Laying"),
                        ("7th" , "Seven Swans a Swimming"),
                        ("8th" , "Eight Maids a Milking"),
                        ("9th" , "Nine Ladies Dancing"),
                        ("10th" , "Ten Lords a Leaping"),
                        ("11th" , "Eleven Pipers Piping"),
                        ("12th", "12 Drummers Drumming")])

# Initialize a counter starting at beginning of the list
counter=0

# Loop through each of the days until we get to 0 from 11
while counter < len(days_gifts):
 
   # Get the actual day from the ordered dictionary
   current_day=days_gifts.keys()[counter]
 
   # Print the intro to the day
   print "On the %s day of Christmas, my true love gave to me:" % (current_day)
 
   # Set up a second counter, because we do a loop within a loop until we get to the partridge in a pear tree.
   # For now, we'll make the second counter the same as the first
   second_counter=counter

   # The second counter is going to move in the other direction.
   # While the first counter is going from the beginning of the dictionary to the end of the dictionary,
   # the second counter is going from the current place to the beginning of the dictionary.
   while second_counter > 0:

      # Print what gifts are for the second counter
      print "%s," % days_gifts.values()[second_counter]
 
      # Decrease the second counter so we get a little closer to the beginning of the list (the 1st day of Christmas)
      second_counter-=1
      
   # Print the partridge in a pear tree by itself
   if counter == 0:
      print "%s\n" % days_gifts.values()[second_counter]
   else:
      print "and\n%s\n" % days_gifts.values()[second_counter]
 
   # Increase first counter to get closer to the end of the list (the 12th day of Christmas)
   counter+=1

Two changes need to happen for that to occur.

First, the inner while loop will end when it’s greater than 0, not greater than or equal to 0, so we do a special case for the last one (the partridge in a pear tree).

We also add in a little if/else at the end. With this if/else logic we’re just saying “If it’s the 1st day, don’t put in the ‘and”; otherwise, put in the ‘and.'”

Now, if you run it, it should work as you would normally sing the song.

Leave a Reply

Your email address will not be published. Required fields are marked *