# How can I use \dolistloop from etoolbox with \do redefined in preamble?

I am trying to use \dolistloop from etoolbox with the \do-command redefined in the preamble. However, the redefinition of the \do-command is ignored. If I put it within the document, it works as expected. Here is the example that does not work as intended:

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\alist}{}

\def\birdlist{}

\renewcommand*{\do}[1]{
\appto\birdlist{Bird: #1. }
}

\begin{document}

\dolistloop{\alist}
\birdlist

\end{document}


The output of that code is: ParrotCanary, indicating that \do has not been redefined. If I move the redefinition of \do into the document,

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\alist}{}

\def\birdlist{}

\begin{document}

\renewcommand*{\do}[1]{
\appto\birdlist{Bird: #1. }
}
\dolistloop{\alist}
\birdlist

\end{document}


the result is the expected: Bird: Parrot. Bird: Canary. How can I use the \dolistloop function with a \do-function that is defined in the preamble? (I am working on a project with documents in several subfolders and I want to avoid appending the \renewcommand*{\do}-code in each folder's .tex-files.)

• Welcome to TeX.SE. You can't, at least not this way. \do is redefined in \begin{document} to be \noexpand
– user31729
Mar 2, 2018 at 22:43

I don't recommend using \dolistloop in a global context: \do is a macro that is often redefined, especially in \begin{document} (or in \AtBeginDocument{...} in which it gets the meaning of \noexpand. I also would not use it 'globally' inside \begin{document}.

I suggest to use \forlistloop which requires a handler, but even this can be simplified (see the code)

Rename the personal definition of \do into, say, \dothis and call \forlistloop{\dothis}{\alist} instead

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\alist}{}

\newcommand{\birdlist}{}

\newcommand*{\dothis}[1]{%
\appto\birdlist{Bird: #1. }
}

%Define a wrapper
\newcommand{\fakedolistloop}[1]{%
\forlistloop{\dothis}{#1}%
}

\fakedolistloop{\alist}

% Or call it directly

%\forlistloop{\dothis}{\alist}
\birdlist

\end{document}


The inevitable expl3 solution

\documentclass{article}
\usepackage{etoolbox}

\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \g_rasmus_bird_seq

\clist_map_inline:nn {#1} {
\seq_gput_right:Nn \g_rasmus_bird_seq {##1}
}
}

\NewDocumentCommand{\listloop}{}{
\seq_map_inline:Nn \g_rasmus_bird_seq {\dothis{##1}}
}
\ExplSyntaxOff

\newcommand{\birdlist}{}

\newcommand*{\dothis}[1]{%
\appto\birdlist{Bird: #1. }
}

\begin{document}
\listloop

\birdlist
\end{document}


Next installment

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\alist}{}

\newcommand{\birdlist}{}

\newcommand*{\dothis}[2]{%
\appto{#1}{Bird: #2. }
}

\newcommand{\fakedolistloop}[2]{%
\forlistloop{\dothis{#2}}{#1}%
}

\begin{document}
\newcommand{\otherbirdlist}{}
\fakedolistloop{\alist}{\birdlist}

\fakedolistloop{\alist}{\otherbirdlist}

Showing:

\birdlist

or

\otherbirdlist
\end{document}

• That works. Much appreciated. Would it be a possible to add the list to which we are appending, i.e \birdlist, as an argument to the loop-function? That would add flexibility to the usage of the command. Mar 2, 2018 at 22:59
• @Rasmus: Do you mean \fakelistloop{\alist}{\birdlist}?
– user31729
Mar 2, 2018 at 23:01
• @Rasmus: See the 3rd. version at the end -- did you mean that?
– user31729
Mar 2, 2018 at 23:06
• Ah :-) Great. But I am not sure I understand why that works. It seems that \fakelistloop only takes one argument? Mar 2, 2018 at 23:06
• It seemed to work also in the case where \fakelistloop was defined with only one argument. Which confused me a bit. Mar 2, 2018 at 23:08

You can't use \do that way. It's a scratch macro and you should redefine it at point of usage, as its meaning cannot be relied upon.

A different implementation:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
{
\rasmus_list_add:nn { #1 } { #2 }
}
\NewDocumentCommand{\listmap}{m+m}
{
% define the scratch macro based on the second argument
\cs_set:Nn \__rasmus_list_do:n { #2 }
% map the list using the newly defined macro
\seq_map_function:cN { g_rasmus_list_#1_seq } \__rasmus_list_do:n
}

{
% make sure the list exists
\seq_if_exist:cF { g_rasmus_list_#1_seq }
{
\seq_new:c  { g_rasmus_list_#1_seq }
}
% add the items
\clist_map_inline:nn { #2 }
{
\seq_put_right:cn  { g_rasmus_list_#1_seq } { ##1 }
}
}
\ExplSyntaxOff

\begin{document}

\listmap{alist}{%
}

\listmap{birdlist}{#1\par}

\end{document}


You can add items sequentially or also as a comma separated list, so

\listadd{alist}{Parrot,Canary}


and

\listadd{alist}{Parrot}

As you see, you can use \listmap for different purposes, here for populating a new list and for printing the new list. The current item in the list is simply denoted by #1.