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:
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,
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}

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:
- be usable within alignments,
- allow nesting (up to nine levels),
- not use macros to encapsulate the items but manipulate them as usual macro parameters,
- 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).