About
Shop
LaTeX
Software
Books
Gallery
News
Contact
Blog
Settings
Account


2.7.2 Iterating Over a Comma-Separated List

The etoolbox package [51] provides:

\docsvlist{item1,item2,…}

This iterates over the given comma-separated list and does

\do{item}

at each iteration, where ⟨item⟩ is the current item in the list. It's up to the user to define \do before using \docsvlist. For example:

\renewcommand{\do}[1]{#1. }%
\docsvlist{Parrot,Canary,Zebra,Arara,Duck}

produces:

Parrot. Canary. Zebra. Arara. Duck.

Alternatively you can provide your own handler instead of \do using

\forcsvlist{handler cs}{list}

For example:

\newcommand{\mylistitem}[1]{#1. }%
\forcsvlist{\mylistitem}{Parrot,Canary,Zebra,Arara,Duck}

which again produces:

Parrot. Canary. Zebra. Arara. Duck.

The argument \docsvlist (and \forcsvlist) doesn't get expanded, so if you try:

\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}%
\renewcommand{\do}[1]{#1. }%
\docsvlist{\mylist}

then you'll only have a list with a single item (\mylist) so you'll just have the one iteration

\do{\mylist}

which just produces:

\samplecsvlist.

Instead you need to make sure the argument is expanded before it's processed by \docsvlist:

\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}%
\renewcommand{\do}[1]{#1. }%
\expandafter\docsvlist\expandafter{\mylist}

The \expandafter commands may look a bit confusing but the syntax is

\expandaftertoken 1⟩⟨token 2

This makes TeX expand ⟨token 2⟩ before it processes ⟨token 1⟩, so it's equivalent to:

token 1⟩⟨expansion of token 2
Therefore

\expandafter \docsvlist \expandafter
token 1token 2

means that TeX must expand the thing after \docsvlist before it does \docsvlist (step ① in Figure 2.9), but that thing happens to be another \expandafter:

\expandafter { \mylist
↗ 
token 1token 2

This means that before TeX processes the left brace character { it must first expand the token after it (step ②), so that \mylist is replaced with its definition (step ③).

So TeX starts out with the first \expandafter, skips over \docsvlist ① and does the second \expandafter which makes TeX skip over the open brace ② and expand \mylist, which replaces \mylist with its definition ③.

Figure 2.9: Processing \expandafter
 
Annotated image of how \expandafter is processed.
  1. The first step ① starts with the first \expandafter and "jumps over" \docsvlist to the second \expandafter.
    \expandafter \docsvlist \expandafter { \mylist }
     
     
  2. The second step ② starts on the second \expandafter and jumps over the open curly brace { to \mylist.
    \expandafter \docsvlist \expandafter { \mylist }
         
         
  3. The third step ③ replaces \mylist with its replacement text (one-level expansion).

    \mylist
    Parrot,Canary,Zebra,Arara,Duck

  4. The fourth step ④ returns back to \docsvlist after the first \expandafter.

    \docsvlist{Parrot,Canary,Zebra,Arara,Duck}

End of Image.

Once \mylist has been expanded, TeX then goes back to the \docsvlist command, which is now in the form:

\docsvlist{Parrot,Canary,Zebra,Arara,Duck}

In the case of \forcsvlist, this becomes more complicated as the list is the second argument. However, since the first argument is just a single control sequence it doesn't need to be grouped and can therefore be skipped over with another \expandafter. For example:

\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}%
\newcommand{\mylistitem}[1]{#1. }%
\expandafter\forcsvlist\expandafter\mylistitem\expandafter{\mylist}

The datatool package also provides some comma-separated list related commands:

\DTLifinlist{item}{list}{true}{false}

This checks if the given item is in ⟨list⟩. A one-level expansion is performed on ⟨list⟩ but not on ⟨item⟩.

\DTLnumitemsinlist{list}{cs}

This counts the number of non-empty items in ⟨list⟩ and stores the result in the control sequence ⟨cs⟩. Again, a one-level expansion is performed on ⟨list⟩.

Example:

\newcommand{\mylist}{Parrot,Canary,Zebra,Arara,Duck}%
Parrot 
\DTLifinlist{Parrot}{\mylist}{is}{isn't}
in the list.
Number of items in the list: 
\DTLnumitemsinlist{\mylist}{\numitems}\numitems.

produces:

Parrot is in the list. Number of items in the list: 5

Spaces

Remember what I mentioned earlier about being careful of spaces? Here's an illustration of unexpected behaviour involving spaces in lists:

\renewcommand{\do}[1]{``#1''. }%
\docsvlist{ Parrot , Canary , Zebra , Arara , Duck }

This produces:

“Parrot ”. “Canary ”. “Zebra ”. “Arara ”. “Duck ”. End of Image.


The leading spaces have been ignored but the trailing spaces are still present.

The LaTeX kernel has a command that can also iterate through a comma-separated list but it's an internal command:

\@forcs⟩:=⟨list⟩\do{body}

This iterates through ⟨list⟩ and assigns the control sequence ⟨cs⟩ to the current item in the list so that it can be used as a placeholder in ⟨body⟩. Note that in this context \do doesn't refer to etoolbox's \do handler macro but is used as an argument delimiter (so it's like plain TeX syntax rather than LaTeX syntax). Since \@for is an internal command it should only be used in a package or class file. If it has to be used in the document, it should be placed between \makeatletter and \makeatother like this:

\makeatletter
\@for\thisitem:=Parrot,Canary,Zebra,Arara,Duck\do{\thisitem. }
\makeatother

This produces:

Parrot. Canary. Zebra. Arara. Duck.

The \@for command expands the list so:

\newcommand*{\mylist}{Parrot,Canary,Zebra,Arara,Duck}%
\makeatletter
\@for\thisitem:=\mylist\do{\thisitem. }
\makeatother

produces the same result as above. However, now let's look at what happens when we introduce spaces into the list again:

\makeatletter
\@for\thisitem:= Parrot , Canary , Zebra , Arara , Duck \do
{``\thisitem''. }
\makeatother

This produces:

“ Parrot ”. “ Canary ”. “ Zebra ”. “ Arara ”. “ Duck ”. End of Image.


In this case both the leading and trailing spaces have been retained.

The etextools package [15] also provides commands to iterate over lists. For example:

\csvloop[auxiliary commands]{list}

The \csvloop macro iterates over the comma-separated list given in ⟨list⟩ (which may be a macro that expands to a list) and at each iteration does ⟨auxiliary commands⟩. If this optional argument is missing, \do is assumed, which behaves in the same way as with etoolbox's \docsvlist command. So

\renewcommand{\do}[1]{#1. }%
\csvloop{Parrot,Canary,Zebra,Arara,Duck}

produces

Parrot. Canary. Zebra. Arara. Duck.

Now let's try with spaces again:

\renewcommand{\do}[1]{``#1''. }%
\csvloop{ Parrot , Canary , Zebra , Arara , Duck }

This produces:

“ Parrot ”. “Canary ”. “Zebra ”. “Arara ”. “Duck ”. End of Image.


In this case the leading space has been retained on the first item, but not on any of the other items, but all the trailing spaces have been retained.

This section introduces one of etextools commands for illustrative purposes, but some of the commands in etextools and etoolbox conflict. For example, both packages define a command called \forlistloop but the syntax is incompatible. Since datatool automatically loads etoolbox this means that datatool and etextools may also conflict.

Another package is pgffor (part of the pgf bundle [102]) which provides:

\foreachvariables[options] in {list}{body}

where ⟨list⟩ is either an explicit comma-separated list or a control sequence that expands to a comma-separated list. The syntax can get quite complicated, but the simplest version is in the form:

\foreach \thisitem in {Parrot,Canary,Zebra,Arara,Duck}
{\thisitem. }

which produces:

Parrot. Canary. Zebra. Arara. Duck.

Now let's try with spaces:

\foreach \thisitem in { Parrot , Canary , Zebra , Arara , Duck }
{``\thisitem''. }

This produces:

“Parrot ”. “Canary ”. “Zebra ”. “Arara ”. “Duck ”. End of Image.


which is the same result as with \docsvlist where the trailing spaces have been retained but not the leading spaces.

This is why it's important to ensure any spurious spaces are removed from comma-separated lists in your source code. It may be that some commands trim all spaces (for example in the optional argument of \usepackage or \documentclass),6 while some commands only trim leading spaces (as with \docsvlist and \foreach) but others don't trim any spaces (such as \cite, which internally uses \@for). Remember that you can comment out space caused by the EOL character, and spaces at the start of lines are automatically discarded by TeX, so to make your code clearer you can do, for example:

\docsvlist
{%
  Parrot,%
  Canary,%
  Zebra,%
  Arara,%
  Duck%
}

If you comment an EOL character that would naturally be discarded (for example, following a control sequence) there's no harm done, but if you forget to comment an unwanted EOL character, you can end up with weird spaces in your document or an error message.

Empty Items

In addition to watching out for spurious spaces, you also need to consider what happens if you have an empty item in your list. Do you want empty items skipped or should they be processed either in the same way as the other items or by displaying a missing data symbol, such as an em-dash? Let's look at how the above comma-separated list processing commands deal with this type of situation:

\renewcommand{\do}[1]{``#1''. }

\docsvlist{,Parrot,Canary,Zebra,,Arara,Duck}

\csvloop{,Parrot,Canary,Zebra,,Arara,Duck}

\foreach\thisitem in {,Parrot,Canary,Zebra,,Arara,Duck}
{``\thisitem''. }

\makeatletter
\@for\thisitem:=,Parrot,Canary,Zebra,,Arara,Duck\do
{``\thisitem''. }
\makeatother

The result is:

“Parrot”. “Canary”. “Zebra”. “Arara”. “Duck”.

“Parrot”. “Canary”. “Zebra”. “Arara”. “Duck”.

“”. “Parrot”. “Canary”. “Zebra”. “”. “Arara”. “Duck”. “”.

“”. “Parrot”. “Canary”. “Zebra”. “”. “Arara”. “Duck”. “”. End of Image.


So \docsvlist and \csvloop both skip empty items but \foreach and \@for don't.

Exercise 3. Iterating Through a List

Create a document that defines the commands:

\newcommand*{\mylistI}{A,B,C,D}
\newcommand*{\mylistII}{a,b,c,d}

then create the tabular environment shown in Table 2.8 using \docsvlist to construct columns 2 to 5 for each row.


Table 2.8: Lists for Iteration Exercise
List 1: A B C D
List 2: a b c d

Next, redefine \mylistII so the third item is empty and redo the tabular environment. Finally, define a command called \missingdata that does nothing and redefine \mylistII so the third item is \missingdata and redo the tabular environment.

You can download or view the solution to this exercise.



Footnotes

...documentclass),6
In fact, all spaces are stripped from the optional argument of those two commands, so it's technically possible to do, say, \usepackage[dr aft]{graphics} although I don't recommend you do this.

This book is also available as A4 PDF or 12.8cm x 9.6cm PDF or paperback (ISBN 978-1-909440-07-4).

© 2015 Dickimaw Books. "Dickimaw", "Dickimaw Books" and the Dickimaw parrot logo are trademarks. The Dickimaw parrot was painted by Magdalene Pritchett.

Terms of Use Privacy Policy Cookies Site Map FAQs