Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

How can I iterate, loop, process, or otherwise do something, with each element from a comma separated list of things?

This question has been asked perhaps many many times, and the are already lots of very good answers everywhere. However I think it would also be valuable to have a single reference point where all such alternative techniques are explained in detail, with their advantages and disadvantages discussed.

All of plain-TeX, LaTeX, LaTeX3, LuaTeX, Context, etoolbox, pgffor, etc. answers are invited. Please consider explaining, for example:

  • How does the command/method deal with whitespace around items?
  • How does the command/method deal with empty/missing items? (e.g. a,b,,c)
  • How does the command/method deal with a trailing comma?
  • Can the command/method work of a list stored on a macro?
  • Is each iteration evaluated globally or on a local group? (i.e., if a command is defined during an iteration, does the command survive the loop?)
  • Do items get expanded or somehow mangled before being processed?
  • What about lists where items are key=value or some other kind of pairs?
  • Does the method work for lists separated with something other than a comma?
  • Any other thing I should know before deciding to use this method?
share|improve this question

10 Answers 10

The main loop for comma separated lists in LaTeX3 is

\clist_map_inline:nn

The first argument is an explicit list, the second argument tells LaTeX what to do with each item. For instance, we want to print an enumerate environment from the items:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\makeenumerate}{ m }
 {
  \begin{enumerate}
  \clist_map_inline:nn { #1 } { \item \fbox{##1} }
  \end{enumerate}
 }

\ExplSyntaxOff

\begin{document}

\makeenumerate{a, b ,c d, ,e }

\end{document}

image

I used \fbox in order to illustrate some of the features:

  1. spaces are stripped on either side of the items;

  2. empty items are ignored (empty means only spaces between commas);

  3. no expansion is performed on the item.

Note that the current item is denoted by #1 and it's literally available, which is not the case with the usual \@for, where the current item is hidden in a macro. In the code above we have to use double ##1 because we're inside a macro definition.

A similar function is \clist_map_function:nN, which has the advantage of being fully expandable (but only in x full expansion, not f). The above example would be

\NewDocumentCommand{\makeenumerate}{ m }
 {
  \begin{enumerate}
  \clist_map_function:nN { #1 } \xyz_make_item:n
  \end{enumerate}
 }

\cs_new_protected:Npn \xyz_make_item:n #1
 {
  \item \fbox { #1 }
 }

In this case the current item is passed as an argument to the indicated function.

List mappings can be broken; let's say, in the above example, we want to stop processing if an item is \stop:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\makeenumerate}{ m }
 {
  \begin{enumerate}
  \xyz_make_items:n { #1 }
  \end{enumerate}
 }
\cs_new_protected:Npn \xyz_make_items:n #1
 {
  \clist_map_inline:nn { #1 }
   {
    \tl_if_eq:nnTF { ##1 } { \stop }
     {
      \clist_map_break:
     }
     {
      \item \fbox { ##1 }
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\makeenumerate{a, b ,c d, \stop ,e }

\end{document}

enter image description here

Note that complex code shouldn't be used in \NewDocumentCommand, so I defined an auxiliary function for this purpose.

One can also use

\clist_map_break:n

and the argument given to this function will be executed before breaking the mapping.

The same features apply when using

\keys_set:nn { <module> } { <comma list of key-value pairs> }

for evaluating a set of key-value pairs: leading and trailing spaces are ignored as are empty (blank) items.

If the comma separated list is stored in a macro, one can use

\clist_map_inline:Nn
\clist_map_function:NN

with the same ideas. In my opinion, it's bad programming style allowing both inputs and a variant should be defined.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\makeenumerate}{ sm }
 {
  \begin{enumerate}
  \IfBooleanTF{#1}
   {
    \clist_set:NV \l_xyz_input_clist #2
    \xyz_make_items:V \l_xyz_input_clist
   }
   {
    \xyz_make_items:n { #2 }
   }
  \end{enumerate}
 }

\clist_new:N \l_xyz_input_clist
\cs_new_protected:Npn \xyz_make_items:n #1
 {
  \clist_map_inline:nn { #1 }
   {
    \tl_if_eq:nnTF { ##1 } { \stop }
     {
      \clist_map_break:
     }
     {
      \item \fbox { ##1 }
     }
   }
 }
\cs_generate_variant:Nn \xyz_make_items:n { V }
\ExplSyntaxOff

\begin{document}

\newcommand{\mylist}{A, B ,C,}

\makeenumerate{a, b ,c d, \stop ,e }

\makeenumerate*{\mylist}

\end{document}

Setting a variable with the contents of the macro holding the comma separated list is done because this process “normalizes” the comma separated list for better usage in \clist_map_inline:Nn.


If other delimiters are desired, the better method is to go to sequences; use \seq_map_inline:Nn or \seq_map_function:NN after splitting the input into components with

\seq_set_split:Nnn \l_xyz_input_seq { ; } { #1 }

Full example:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\makeenumerate}{ O{,} m }
 {
  \begin{enumerate}
  \xyz_make_items:nn { #1 } { #2 }
  \end{enumerate}
 }

\seq_new:N \l_xyz_input_seq
\cs_new_protected:Npn \xyz_make_items:nn #1 #2
 {
  \seq_set_split:Nnn \l_xyz_input_seq { #1 } { #2 }
  \seq_map_inline:Nn \l_xyz_input_seq
   {
    \tl_if_eq:nnTF { ##1 } { \stop }
     {
      \seq_map_break:
     }
     {
      \item \fbox { ##1 }
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\makeenumerate[;]{a; b ;c, d; \stop ;e }

\end{document}

enter image description here

Also here the splitting ignores leading and trailing spaces and also empty items.

share|improve this answer
    
Nice! Although for key=value items I would mention \keyval_parse:NNn instead, as this is the actual function doing just the parsing (instead of the more high-level option/key handling). –  Juan A. Navarro Feb 8 at 12:05
1  
@JuanA.Navarro That is in the “Low level interface” section, so it should be used only for extending the key processing to other situations. –  egreg Feb 8 at 12:11

There is a package lambda-lists by Alan Jeffrey for "Lists in TeX's mouth". Here's an example in plain TeX:

\catcode`@=11 \input lambda.sty \catcode`@=12
\tracingmacros=1 % check out the log file afterwards!

\def\mylist{\Listize[1,  2 ,3,,4=foo,5,]}
\def\myfun#1{do something with #1\def\foo{}}
\def\fooDefined?{\ifdefined\foo foo's defined\else foo's undefined\fi}

\Unlistize\mylist\par

{\Unlistize{\Map\myfun\mylist}\par}
% or, \def\myfun#1{{do something with #1\def\foo{}}}
\fooDefined?

\Unlistize{\Map\myfun\mylist}\par
\fooDefined?

\def\mylist{\Listize[1,2,3,4,5]} % would error with non-numeric entries w/ below
\Unlistize{\Map\myfun{\Filter{\Lessthan3}\mylist}}

\bye

enter image description here

share|improve this answer

This is using the under-development tabstackengine package, first introduced here at Writing a table with equally spaced columns, based on the widest column (source code available at Measuring align).

The package extends the stackengine package by adding tabbing capability. This answer, Can I tab inside of align environment?, gives some of the syntax of the package. I apologize that I have been lax in completing the package and getting it out the door, so the only way to see it in action is by searching this site for tabstackengine.


One of the nice bonuses of tabstackengine is that it gives you access to its parser. By default, the tabbing delimiter is a &. However, it can be reset to a , for the purposes of this question. It provides \readTABrow{ID}{tab-separated-string} for digesting a list and assigning it an ID. It also provides \TABcells{ID} to tell you how many items are in the list named ID, and \TABcell{ID}{column number} to provide the element for the column in question from the ID'ed row.

Iterating over it, as the OP asks, is merely a question of using the etoolbox \whileboolexpr facility to process the information.

Method Highlights:

  1. Leading/trailing spaces are stripped, when using the high-level macro \TABcell{}{}; however...

  2. spaces can be preserved by using a lower level syntax, such as \csname TABX\ident X\romannumeral 3\endcsname for item 3 of dataset \ident.

  3. Trailing comma creates a new blank element for the last data item.

  4. Null cells are kept and given null value.

  5. Tabbing delimiter is user definable, including a space token.

  6. data is not expanded and can be accessed in its raw form; however, a processed form of the unexpanded data is also available in which leading/trailing spaces are removed and optionally placed in an \ensuremath wrapper (the latter occurs when \TABstackMath is invoked in advance).

  7. Because the method stores the parsed data in unique identifiers for later recall, it conveniently allows multiple datasets to be processed and combined after the fact (this is a major strength of the approach). This is, to the core, what tabstackengine was designed to do: it reads and digests arrayed text, row by row, but outputs it by column by column, in the form of adjacent stacks.

\documentclass[12pt]{article}
\usepackage[T1]{fontenc}
\usepackage{tabstackengine}
\usepackage{etoolbox}
\parskip 1ex
\parindent 0ex
\begin{document}
\TABstackMath
\def\x{1,2,   3, 4, 5,x,,8,\textbf{9},}
\def\ident{mydata}
Here is the data: \fbox{\x} identified with the tag \ident

\setstackTAB{,}
\def\readdata{\readTABrow{\ident}}
\expandafter\readdata\expandafter{\x}
\TABcells{\ident} cells in \ident

For dataset \ident, the cell data for cells 3, 4, and 9 are
:\TABcell{\ident}{3}:\TABcell{\ident}{4}:\TABcell{\ident}{9}:

Spaces can be preserved by calling on low-level macro.  
Item 3 is :\csname TABX\ident X\romannumeral 3\endcsname:

Here is a loop through \ident, showing raw and processed items:\\
\newcounter{index}
\setcounter{index}{0}
\whileboolexpr{test {\ifnumless{\theindex}{\TABcells{\ident}}}}{%
  \stepcounter{index}%
  :\csname TABX\ident X\romannumeral\theindex\endcsname%
  :\TABcell{\ident}{\theindex}:\\%
}

Here is \textbackslash TABcell\{\textbackslash ident\}\{9\} detokenized:\\
\detokenize\expandafter{\TABcell{\ident}{9}}

Here is the lowlevel macro \textbackslash TABXmydataXix detokenized:\\
\detokenize\expandafter{\TABXmydataXix}
\end{document}  

enter image description here

share|improve this answer

Package kvsetkeys

  • Package kvsetkeys provides parsers for key value lists and comma separated lists.
  • Many formats are supported: LaTeX, plain TeX, it works even with iniTeX.
  • Syntax:

    \comma@parse{⟨comma separated list⟩}{⟨code/processor with one argument⟩}
    

Example

The example is given for iniTeX to show the minimal requirements of the package.

% Setup for iniTeX
\catcode`\{=1
\catcode`\}=2
\catcode`\#=6
\catcode`\@=11

% Package is loaded; LaTeX: \usepackage{kvsetkeys}
\input kvsetkeys.sty

% -------------------------------------------------------------------
% Call with entry processor that takes the entry as argument
% -------------------------------------------------------------------
\def\ProcessEntry#1{\message{<#1>}}
\comma@parse{a, b,, c ,d,}\ProcessEntry

% Result: <a> <b> <c> <d>

% -------------------------------------------------------------------
% Funny entries
% -------------------------------------------------------------------
\long\def\@gobble#1{}% from LaTeX
\def\@empty{}% from LaTeX
\comma@parse{
  a, #1, \fi, \iffalse, {b}, {{c}}, d e f, {}, \empty, { }, {g,h}
}{%
  \immediate\write16{[\meaning\comma@entry]}%
  \@gobble
}

% Result:
%   [macro:->a]
%   [macro:->##1]
%   [macro:->\fi ]
%   [macro:->\iffalse ]
%   [macro:->b]
%   [macro:->{c}]
%   [macro:->d e f]
%   [macro:->\empty ]
%   [macro:-> ]
%   [macro:->g,h]

% -------------------------------------------------------------------
% \comma@break allows the premature ending of the list processing
% -------------------------------------------------------------------
\comma@parse{
  2,3,5,7,0,1,4
}{%
  \ifnum\comma@entry=0 %
    \comma@break
  \else
    \message{<\comma@entry>}%
  \fi
  \@gobble
}

% Result: <2> <3> <5> <7>

% -------------------------------------------------------------------
% Active commas are supported as separators
% -------------------------------------------------------------------
\catcode`\,=13 % active
\def,{;}
\comma@parse{a,b,c}{\message{(\comma@entry)}\@gobble}

% Result: (a) (b) (c)

% End of job
\csname @@end\endcsname\end

Answer to the questions

  • How does the command/method deal with whitespace around items?

Spaces (also more than one) are removed around items.

  • How does the command/method deal with empty/missing items? (e.g. a,b,,c)

Empty items are ignored. An empty item can be specified using a pair of curly braces: a, {}, b

  • How does the command/method deal with a trailing comma?

This is interpreted as an empty last item, which is handled as other empty items by ignoring, see previous answer.

  • Can the command/method work of a list stored on a macro?

Since the list is the first argument of \comma@parse, a list macro can easily unpacked with \expandafter:

\def\list{a,b,c}
\expandafter\comma@parse\expandafter{\list}{...}
  • Is each iteration evaluated globally or on a local group? (i.e., if a command is defined during an iteration, does the command survive the loop?)

The loop is not put into a local group, the current group level is not changed. Thus assignments including definitions survive the loop.

  • Do items get expanded or somehow mangled before being processed?

No. The entry is directly available, if a processor with argument is used for \comma@parse. Otherwise the entry is stored and available in macro \comma@entry.

If the value is wrapped into curly braces, then one level of curly braces is removed exactly. This way, by putting the item in a pair of curly braces, the item can be empty, can contain spaces or commas and further properly nested curly braces are not touched.

  • What about lists where items are key=value or some other kind of pairs?

Package kvsetkeys also supports lists with key value pairs.

  • Does the method work for lists separated with something other than a comma?

Active commas (and active equal signs for key value processing) are supported. But other symbols cannot be used as separators.

  • Any other thing I should know before deciding to use this method?
  • \comma@break implements a shortcut for ending the list parsing, if the following entries are not needed any more.
  • Currently entries with \par break \comma@parse. This will be supported in the next version.
share|improve this answer

The etoolbox package provides some list processing tools. For comma-separated lists there are

  • \docsvlist{<list>} where each item is put in the input stream with \do{<item>}. This means a suitable definition of \do has to be provided.
  • \forcsvlist{<handler>}{<list>} where each item is put in the input stream as argument to <handler> which can be arbitrary code provided its last token has a mandatory argument.

The following example shows that empty items are stripped from the list. For each item leading spaces are removed, trailing spaces aren't!

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newcommand\makeenumerate[1]{%
  \begin{enumerate}
    \forcsvlist{\makeenumerate@item}{#1}
  \end{enumerate}
}
\newcommand\makeenumerate@item[1]{\item \fbox{#1}}
\makeatother

\begin{document}

\makeenumerate{a, b ,c d, ,e }

\end{document}

enter image description here

For lists with other separators parsers can be defined with

\DeclareListParser*{<macro>}{<separator>}

both above mentioned parsers are defined with it:

\DeclareListParser{\docsvlist}{,}
\DeclareListParser*{\forcsvlist}{,}

An example as variation of the above example:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\DeclareListParser*\formylist{;}
\newcommand\makeenumerate[1]{%
  \begin{enumerate}
    \formylist{\makeenumerate@item}{#1}
  \end{enumerate}
}
\newcommand\makeenumerate@item[1]{\item \fbox{#1}}
\makeatother

\begin{document}

\makeenumerate{a; b ;c d; ;e }

\end{document}

For list processing not dealing with user input directly etoolbox provides quite a number of different macros. All of them deal with macros that hold the list. The next variation of the above example shows \listadd{<list>}{<item>} and \forlistloop{<handler>}{<list>} where both times <list> means a macro holding the etoolbox list:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newcommand*\mylist{}
\newcommand\makeenumerate[1]{%
  \forcsvlist{\listadd\mylist}{#1}%
  \begin{enumerate}
    \forlistloop{\makeenumerate@item}{\mylist}
  \end{enumerate}
}
\newcommand\makeenumerate@item[1]{\item \fbox{#1}}
\makeatother

\begin{document}

\makeenumerate{a, b ,c d, ,e }

\end{document}

There are quite a number of additional macros (e.g. \listeadd for adding expanded items, or conditionals like \ifinlist ...) which are all explained in the manual.


In all of etoolbox's list parsers \listbreak can be used to break the loop:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newcommand*\mylist{}
\newcommand\makeenumerate[1]{%
  \forcsvlist{\listadd\mylist}{#1}%
  \begin{enumerate}
    \forlistloop{\makeenumerate@item}{\mylist}
  \end{enumerate}
}
\newcommand\makeenumerate@item[1]{%
  \ifstrequal{#1}{\stop}
    {\listbreak}
    {\item \fbox{#1}}%
}
\makeatother

\begin{document}

\makeenumerate{a, b ,c d, \stop ,e }

\end{document}

enter image description here

share|improve this answer

Using egreg's template for straight LaTeX

\documentclass{article}

\makeatletter
\newcommand{\makeenumerate}[2][,]{\begin{enumerate}%
  \def\dolist##1{\expandafter\@dolist##1#1\@eol}%
  \def\@dolist##1#1##2\@eol{%
    \begingroup\setbox0=\hbox{##1\unskip}\ifdim\wd0=0pt\endgroup\else\endgroup\item \fbox{\ignorespaces##1\unskip}\fi%
    \ifx\@eol##2\@eol\else\@dolist##2\@eol\fi}%
  \dolist{#2}%
\end{enumerate}}
\makeatother

\begin{document}

\makeenumerate{a, b ,c d, ,e }

\makeenumerate[;]{a; b ;c d; ;e }
\end{document}

enter image description here

  • How does the command/method deal with whitespace around items: It strips leading and trailing spaces
  • How does the command/method deal with empty/missing items: It ignores them
  • How does the command/method deal with a trailing comma: It ignores it
  • Can the command/method work of a list stored on a macro: No
  • Is each iteration evaluated globally or on a local group: Locally
  • Do items get expanded or somehow mangled before being processed: Yes, they get expanded into box0
  • What about lists where items are key=value or some other kind of pairs: Shouldn't matter
  • Does the method work for lists separated with something other than a comma: Yes
  • Any other thing I should know before deciding to use this method: If you are not trying to ignore empty items, you don't run into the box0 issues
share|improve this answer

The old standard LaTeX \@for. I don't claim expertise on it, it's just what I came across as a solution for my limited applications.

Example from my thesis class:

\documentclass{article}

\makeatletter
\newcommand{\committeemembers}[1]{\providecommand\ttu@committeemembers{}\renewcommand{\ttu@committeemembers}{#1}}
\committeemembers{Member1=Name,Member2=Name,Member3=Name}
\makeatother

\begin{document}

\makeatletter
\newcounter{mycounter}
%\@for\themember:={Member 1 Name,Member 2 Name,Member 3 Name}\do{\stepcounter{mycounter} \themember{} \themycounter }
\@for\themember:=\ttu@committeemembers\do{\fbox{\themember}}
\makeatother

\end{document}

Q&A:

  1. How does the command/method deal with whitespace around items? Whitespace is preserved.
  2. How does the command/method deal with empty/missing items (e.g. a,b,,c)? Missing items are iterated over.
  3. How does the command/method deal with a trailing comma? Trailing commas are treated like missing items.
  4. Can the command/method work of a list stored on a macro? Yes.
  5. Is each iteration evaluated globally or on a local group? (i.e., if a command is defined during an iteration, does the command survive the loop?) Yes.
  6. Do items get expanded or somehow mangled before being processed? Don't think so on mangling, not sure on expansion.
  7. What about lists where items are key=value or some other kind of pairs? No difference, any items can be separated by commas.
  8. Does the method work for lists separated with something other than a comma? Only commas are considered separators.
  9. Any other thing I should know before deciding to use this method? No idea. I assume every other option is more full-featured, and this one is the least common denominator.
share|improve this answer

The xinttools package provides various loops, and among them there is \xintFor which iterates over a comma separated lists of items (possibly represented as macro). The package can be used either with Plain Tex:

\input xinttools.sty

or with LaTeX

\usepackage{xinttools}

Loading and usage is possible without the Plain format loaded as long as:

  1. for loading of the package: the % is of category code comment character, the \ is of category code escape character, digits have category code other, letters have category code letter,

  2. for usage of \xintFor: {, }, , and # all have their standard category code, and the \empty and \space macros their standard meanings.

The syntax is:

\xintFor #1 in {\mylist} \do {stuff with #1}

The #1 (or #2, ..., #9: this is to allow nesting with the iterated text using itself a further \xintFor) is treated as in the replacement text of a macro with parameter, thus if \list expands to, or is, abc, def, ghk then #1 will be abc then def then ghk not a macro expanding to these things.

When \xintFor is used inside a macro definition, the # must be doubled as usual: ##1. There will be no conflict if this macro once defined is used elsewhere, for example inside another \xintFor loop defined itself with a #1 or ##1.

The \xintFor is not expandable, that means one can not insert it inside an \edef. However it has been especially designed to be usable inside alignments such as a tabular.

\documentclass{article}
\usepackage{xinttools}

\begin{document}    
\begin{tabular}{ccc}
\hline
  \xintFor #1 in {A, B, C, D, E}\do {#1&\lowercase{#1}&\textbf{#1}\\}
\hline
\end{tabular}

\end{document}

tabular xinttools example

Although the loop survives the closing of groups possibly done by its replacement text (as is the case in the example above), it does not make by itself any global assignment.

  • How does the command/method deal with whitespace around items?

It removes them, both starting and trailing spaces (the case of multiple space tokens, of standard character code 32, is also handled). Additionally if the item was ...,<spaces>{item}<spaces>,..., one level of brace pair is removed (but spaces inside the braces are left untouched). In the special case ...,{stuff},.. of a braced item with no spaces to separate both commas from braces, stuff will also be stripped of starting and trailing spaces.

  • How does the command/method deal with empty/missing items? (e.g. a,b,,c)

A missing item is treated as empty item. That is #1 is empty. The iteration is not skipped.

  • How does the command/method deal with a trailing comma?

A trailing comma means a trailing empty item. One can use \xintifForLast to abort the loop if one knows the list has such an empty ending item!

  • Can the command/method work of a list stored on a macro?

Yes. \xintFor #1 in \list \do, \xintFor #1 in {\list} \do, \xintFor #1 in {a,b,c} \do are equivalent (if the macro \list expanding to a,b,c has arguments, braces around it and its arguments must be used). And \list is expanded exactly once. With in {\x,a,b,c} as input, \x will be expanded, and may for example transform the list into {d,e,f,a,b,c}. Use {{\x},a,b,c} or { \x,a,b,c} to prevent this expansion of \x (in the latter form, the initial space is gobbled and serves only to prevent expansion of \x). For all other items except the first, there will never be any expansion.

  • Is each iteration evaluated globally or on a local group? (i.e., if a command is defined during an iteration, does the command survive the loop?)

No global assignments is made. No local group is created. Yes, if a command is defined during the iteration it will survive the loop: assuming the iteration text does not itself close groups!

  • Do items get expanded or somehow mangled before being processed?

Spaces at the starts and ends are stripped, and exactly one level of braces is removed. The first item will get expanded if it is not protected by braces or an intial space and if the list is not given as macro but directly.

  • What about lists where items are key=value or some other kind of pairs?

\xintFor only looks at commas, thus the #1 will be key=value.

  • Does the method work for lists separated with something other than a comma?

No, \xintFor is only for comma separated lists (with a standard category code comma). \xintFor converts the comma separated list to a sequence of braced stripped items using \xintCSVtoList and then treats this sequence with \xintFor*.

  • Any other thing I should know before deciding to use this method?

The comma separated items may contain \par or empty lines.

\xintFor has been designed to:

  1. be usable within alignments,
  2. allow nesting (up to nine levels),
  3. not use macros to encapsulate the items but manipulate them as usual macro parameters,
  4. not create groups.

The xint documentation provides some more information.

The package does not provide facilities to manipulate comma separated lists per se, only to use them.

Expandable manipulations are possible via first converting with \xintCSVtoList the comma separated list to a sequence of braced items ({abc}{123}...) and then using \xintApply or \xintApplyUnbraced which apply a given one-argument macro in turn to each item of the sequence. Also, \xintNthElt accesses the element of given index (either from the start or end, if negative), but the package is lacking currently facilities to cut and glue such sequences of braced items, perhaps this will be added.

The iteration done by \xintFor can be prematurely interrupted using \xintBreakFor and \xintBreakForAndDo inside the replacement text, for example in the YES or NO branch of some LaTeX-like test, the package own tests, or the etoolbox tests, or some ifthen test (non-expandability not being an issue).

\xintifForFirst {stuff if first iteration}{stuff if not first}, \xintifForLast {stuff if last iteration}{stuff is not last iteration}, allow special treatment of the first or last iterations; but to count iterations the user is invited to define a counter or \count and update it as part of the loop (if used in an alignment, presumably the \count assignments will need to be made global).

share|improve this answer

Presenting the powerful pgffor!

How to use it

pgffor is part of the pgf-bundle and may be familiar to people who already used TikZ. The package defines the \foreach macro, which allows you to iterate over comma separated lists. It is available to nearly all flavours of TeX.

\usepackage{pgffor} % LaTeX
\input pgffor.tex   % plain TeX
\usemodule[pgffor]  % ConTeXt

Usage example

The usage and the several options to \foreach are described in section 56 on page 504 of the TikZ 2.10 manual. Here is one example from the manual, which outputs the elements of the list one by one in square brackets.

\foreach \x in {1,2,3,0} {[\x]}

Answering the questions

  • How does the command/method deal with whitespace around items?

Whitespace is ignored, unless you wrap your item in curly braces. Thus

\foreach \i in { a , b } != \foreach \i in {{ a },{ b }}
  • How does the command/method deal with empty/missing items? (e.g. a,b,,c)

For missing items the loop body is executed, but the loop variable is empty (see MWE)

  • How does the command/method deal with a trailing comma?

A trailing comma is handled like a missing element.

  • Can the command/method work of a list stored on a macro?

Yes and no. You can use a macro instead of a list of elements, but you cannot use a macro inside a list of elements.

While this works

\def\mylist{1,2,3}
\foreach \i in \mylist {...}

this in contrast, will handle the macro as a single element (i.e., it is not expanded while reading this list)

\def\mylist{1,2,3}
\foreach \i in {0,\mylist,4} {...}
  • Is each iteration evaluated globally or on a local group? (i.e., if a command is defined during an iteration, does the command survive the loop?)

The loop body is grouped. You can of course still make macros defined inside \global.

  • Do items get expanded or somehow mangled before being processed?

If an item is a macro, it gets expanded. If an item is a mathematical statement, you can use the evaluate option (see manual) to process the expression.

  • What about lists where items are key=value or some other kind of pairs?

You can define arbitrarily long tuples by separating the items with /.

\foreach \x/\y/\z in {0/0/0, 1/0/0} {...}
  • Does the method work for lists separated with something other than a comma?

No. Comma is the one and only separator.

A longer example

Use any plain TeX engine, e.g. pdftex.

\nopagenumbers% for cropping
\input pgffor.tex
\def\mylist{f,g,h}
Lorem ipsum
% Showcase for preserving whitespace, missing elements, trailing commas and a macro as an item
\foreach \i in { { a } , b , {c,d} ,, e , \mylist ,} {
    \item{$\bullet$} \i
}\par
dolor sit amet
% iterating over a macro and defining a macro inside
\foreach \i in \mylist {
    \def\lastitem{\i}
    \expandafter\gdef\expandafter\glastitem\expandafter{\i}
    \item{$\bullet$} \i
}\par
consectetur, adipisci velit

{\tt\string\lastitem=\meaning\lastitem}

{\tt\string\glastitem=\meaning\glastitem}
% tuples
\foreach \i/\j in {a/1, b/2, c/3} {
    \item{\i} \j
}
\bye

enter image description here

share|improve this answer

Since this question was lacking a luatex solution, I've always wanted to give it luatex a proper try and this seemed to be manageable problem, here is my lualatex solution.

\documentclass{article}

\usepackage{luacode}
\newcommand\makeenumerate[1]{
\begin{enumerate}
\luaexec{
    function split(s, delimiter)
        result = {};
        for match in (s..delimiter):gmatch("(.-)"..delimiter) do
            table.insert(result, match);
        end
        return result;
    end
    list = "#1"
    table = split(list, ",")
    for k,v in pairs(table) do
        tex.print("\\item \\fbox{" .. v .. "}")
    end
}
\end{enumerate}
}
\begin{document}
\makeenumerate{a, b, c, d, e}
\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.