Recipe 4.2 Printing a List with Commas

4.2.1 Problem

You'd like to print out a list containing an unknown number of elements, placing an "and" before the last element and commas between each element if there are more than two.

4.2.2 Solution

Use this function, which returns the formatted string:

sub commify_series {
    (@_ =  = 0) ? ''                                      :
    (@_ =  = 1) ? $_[0]                                   :
    (@_ =  = 2) ? join(" and ", @_)                       :
                join(", ", @_[0 .. ($#_-1)], "and $_[-1]");

4.2.3 Discussion

It often looks odd to print out arrays:

@array = ("red", "yellow", "green");
print "I have ", @array, " marbles.\n";
print "I have @array marbles.\n";
I have redyellowgreen marbles.
I have red yellow green marbles.

What you really want it to say is, "I have red, yellow, and green marbles". The function given in the solution generates strings in that format. The word "and" is placed between the last two list elements. If there are more than two elements in the list, a comma is placed between every element.

Example 4-1 gives a complete demonstration of the function, with one addition: if any element in the list already contains a comma, a semicolon is used for the separator character instead.

Example 4-1. commify_series
  #!/usr/bin/perl -w
  # commify_series - show proper comma insertion in list output
  # @lists is an array of (references to anonymous) arrays
  @lists = (
      [ 'just one thing' ],
      [ qw(Mutt Jeff) ],
      [ qw(Peter Paul Mary) ],
      [ 'To our parents', 'Mother Theresa', 'God' ],
      [ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
      [ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
      [ 'recycle tired, old phrases', 
        'ponder big, happy thoughts', 
        'sleep and dream peacefully' ],
  foreach $aref (@lists) {
      print "The list is: " . commify_series(@$aref) . ".\n";
  # demo for single list
  @list = qw(one two three);
  print "The last list is: " . commify_series(@list) . ".\n";
  sub commify_series {
      my $sepchar = grep(/,/ => @_) ? ";" : ",";
      (@_ =  = 0) ? ''                                      :
      (@_ =  = 1) ? $_[0]                                   :
      (@_ =  = 2) ? join(" and ", @_)                       :
                  join("$sepchar ", @_[0 .. ($#_-1)], "and $_[-1]");

Here's the output from the program:

The list is: just one thing.
The list is: Mutt and Jeff.
The list is: Peter, Paul, and Mary.
The list is: To our parents, Mother Theresa, and God.
The list is: pastrami, ham and cheese, peanut butter and jelly, and tuna.
The list is: recycle tired, old phrases and ponder big, happy thoughts.
The list is: recycle tired, old phrases; ponder 
   big, happy thoughts; and sleep and dream peacefully.
The last list is: one, two, and three.

As you see, we don't follow the ill-advised practice of omitting the final comma from a series under any circumstances. To do so introduces unfortunate ambiguities and unjustifiable exceptions. The examples shown would have claimed that we were the offspring of Mother Teresa and God, and would have had us eating sandwiches made of jelly and tuna fish mixed together atop the peanut butter.

4.2.4 See Also

Fowler's Modern English Usage; we explain the nested list syntax in Recipe 11.1; the grep function in perlfunc(1) and Chapter 29 of Programming Perl; the conditional operator ("?:") is discussed in perlop(1) and in the "Conditional Operator" section of Chapter 3 of Programming Perl