7

I am trying to populate a tabular environment with an etoolbox list:

\documentclass{article}

\usepackage{etoolbox}

\listadd\mylist{Ana}%
\listadd\mylist{Bob}%
\listadd\mylist{Cole}%

\newcommand*{\pco}{%
    \renewcommand*{\do}[1]{Person: & ##1 \\}
    \dolistloop{\mylist}
}

\begin{document}
    \begin{tabular}{r | l}
        \pco
    \end{tabular}
\end{document}

I meant for this to generate a table like:

Person: | Ana
Person: | Bob
Person: | Cole

Instead, I got:

Person: | Ana
BobCole |

I'm having a hard time figuring this out. My first thought was that the \do command is being understood by the table as a single token (i.e. {Person: & ##1 \\}). But in the first instance that's not the case: the first item on the list is successfully split into two columns. However, the following items aren't; indeed, \do seems to be ignored entirely, and the value of each list item is simply being printed.

What am I missing?

  • 1
    The problem is that \pco is called within the first cell of the table. This means the redefinition of \do is forgotten after the first cell ends (cells form groups)… This is why the suggestion with\forlistloop works: the definition of \pcodo is outside of the table. – clemens Jun 9 '17 at 12:46
4

Use \forlistloop instead:

\documentclass{article}

\usepackage{etoolbox}

\newcommand{\pcodo}[1]{Person: & #1 \\}

\listadd\mylist{Ana}
\listadd\mylist{Bob}
\listadd\mylist{Cole}

\newcommand*{\pco}{\forlistloop{\pcodo}{\mylist}}

\begin{document}

\begin{tabular}{r | l}
\pco
\end{tabular}

\end{document}
2

With tabulars, it is often easier to build up (i.e., successively concatenate) a token list and then dump it into the input stream at the end of the process.

\documentclass{article}

\usepackage{etoolbox}

\listadd\mylist{Ana}%
\listadd\mylist{Bob}%
\listadd\mylist{Cole}%

\newtoks\tabtoks
\newcommand*{\pco}{%
    \tabtoks{}%
    \newcommand*{\ZaZa}[1]{\tabtoks\expandafter{\the\tabtoks Person: & ##1 \\}}%
    \forlistloop\ZaZa{\mylist}%
    \the\tabtoks%
}

\begin{document}
    \begin{tabular}{r | l}
        \pco
    \end{tabular}
\end{document}

enter image description here

However, thanks to jfbu for noting the problem can be solved more simply than that. The problem with the OP's code is that the & column separator creates a different scope under \halign, and so one must work within that constraint of making things globally available to the other tabular cells. For example, in the OP's original code, merely changing \renewcommand*{\do}[1]{Person: & ##1 \\} to \gdef\do##1{Person: & ##1 \\} makes the code produce the proper output. However, thanks to egreg for noting that redefining \do globally is not acceptable and pointing to \forlistloop as the optimal alternative.

jfbu proposed the following resolution:

\newcommand*{\pco}{%
  \gdef\ZaZa##1{Person: & ##1 \\}%
  \forlistloop\ZaZa{\mylist}%
}

resulting in code that works.

\documentclass{article}

\usepackage{etoolbox}

\listadd\mylist{Ana}%
\listadd\mylist{Bob}%
\listadd\mylist{Cole}%

\newcommand*{\pco}{%
  \gdef\ZaZa##1{Person: & ##1 \\}%
  \forlistloop\ZaZa{\mylist}%
}

\begin{document}
    \begin{tabular}{r | l}
        \pco
    \end{tabular}
\end{document}
  • @jfbu Do you wish to set a different answer, or for me to add your point to this one? I recommend you provide a separate answer, as I could not do the explanation justice. – Steven B. Segletes Jun 9 '17 at 11:42
  • well done ;-) ... – user4686 Jun 9 '17 at 11:55
  • 1
    Doing \gdef\do is wrong, sorry. It's a local scratch macro and should remain such. – egreg Jun 9 '17 at 12:02
  • @egreg Would \let\svdo\do% before the tabular and \let\do\svdo% afterwards satisfy the need? Also, that would not be needed in the \tabtoks approach, since the scope is limited there... – Steven B. Segletes Jun 9 '17 at 12:08
  • @StevenB.Segletes No. But you can use a different macro with more advanced list management in etoolbox. – egreg Jun 9 '17 at 12:11
2

Alongside the excellent other two answers, I can propose this one which needs no definition of an extra macro:

\documentclass{article}

\usepackage{xinttools}

\newcommand\mylist{Ana, Bob, Cole}

\begin{document}
    \begin{tabular}{r | l}
        \xintFor #1 in \mylist \do{Person: & #1 \\}
    \end{tabular}

% or \xintFor #1 in {Ana, Bob, Cole}
% or \xintFor* #1 in {{Ana}{Bob}{Cole}}
\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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