Printing a List with Commas

Problem

You’d like to print out a list with an unknown number of elements with an “and” before the last element, and with commas between each element if there are more than two.

Solution

Use this function, which returns the formatted string:

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

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 semi-colon 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 = (
    [ '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";
} 

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.

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 above would have claimed that we were the offspring of Mother Theresa and God, and would have had us eating sandwiches made of jelly and tuna fish fixed together atop the peanut butter.

See Also

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

Get Perl Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.