5

I wish to loop over this code:

\begin{tabular}{l*{8}c}
    first column &
    column a &
    column b &
    column c &
    column d &
    column e &
    column f &
    column g
\end{tabular}

But the following does not compile:

\begin{tabular}{l*{8}c}
    first column
    \foreach \i in {a,b,c,d,e,f,g,h}{%
        & column \i%
    }
\end{tabular}

with an error that looks nothing like something I've written:

! Extra }, or forgotten \endgroup.
<template> \unskip \hfil }
                          \hskip \tabcolsep \endtemplate 
l.40         }

I've hope for a while that this post would help, but I'm not sure that this part of the question was finally answered there.

How do I make this loop work?

5
  • 2
    essentially you can't make it work using \foreach each table cell is a group and & is like }{ which ends one group and starts a new one, at which point the internal state of the loop is lost. You either need a different loop system that doesn't require local definitions or just build up a macro with the table row then execute that macro at the end of the loop. May 18, 2018 at 14:58
  • @DavidCarlisle Okay, thus the error message about \endgroup :) What "different loop system" are you thinking about? Also, I'm not sure I understand what you mean by "build a macro with the table row" :\
    – iago-lito
    May 18, 2018 at 15:11
  • as in the answer in the question that you reference. I have some answers building table rows on site no time to look now, I may answer later if no one else does May 18, 2018 at 15:15
  • @DavidCarlisle Okay, thanks, I'll have a closer look on my side :)
    – iago-lito
    May 18, 2018 at 15:20
  • Evidently a great question! So many answers in such diversity! Also, I didn't notice your link, but turns out I did actually something along those lines! May 18, 2018 at 18:22

3 Answers 3

6

You can get a very handy syntax with xparse and expl3. In the second argument #1 stands as a placeholder for the various items in the list.

\documentclass[a4paper]{article}
\usepackage[margin=1.5cm]{geometry}
\usepackage{booktabs}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\firstrow}{mmm}
 {%#1 = first column
  %#2 = format for the other columns
  %#3 = comma separated list of values
  \cs_gset_protected:Nn \__iagolito_temp:n { & #2 }
  #1 \clist_map_function:nN { #3 } \__iagolito_temp:n
 }
\ExplSyntaxOff

\begin{document}

\begin{tabular}{l*{8}c}
\firstrow{first column}{column #1}{a,b,c,d,e,f,g,h} \\
x & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
\end{tabular}

\bigskip

\begin{tabular}{l*{8}c}
\toprule
\firstrow{first column}
  {\begin{tabular}[t]{@{}c@{}}column\\ #1\end{tabular}}
  {a,b,c,d,e,f,g,h} \\
\midrule
x & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\
\bottomrule
\end{tabular}

\end{document}

enter image description here

3
  • Well, cheers :) xparse definitely is powerful! This is the option I like best because it looks the most flexible.
    – iago-lito
    May 28, 2018 at 8:12
  • 2
    I can't understand it my answer had far more zzz than yours but you get the tick :( May 28, 2018 at 8:35
  • @DavidCarlisle <3 ^ ^
    – iago-lito
    May 28, 2018 at 9:26
3

Essentially you can't make it work using \foreach: each table cell is a group and & is like }{ which ends one group and starts a new one, at which point the internal state of the loop is lost. You either need a different loop system that doesn't require local definitions or (like the answer to the question you reference) just build up a macro with the table row then execute that macro at the end of the loop.

This shows both mechanisms, note that in the second one the loop data is managed on the parameter stack and no local (or global) definitions are made, which makes it easier to do the loop "inside" the table.

enter image description here

\documentclass[a4paper]{article}
\usepackage{tikz}
\addtolength\textwidth{2in}
\addtolength\oddsidemargin{-1in}
\begin{document}
\small\centering
\def\zz{first column}
    \foreach \i in {a,b,c,d,e,f,g,h}{%
      \xdef\zz{\zz & column \i}%
    }
\begin{tabular}{l*{8}c}
    \zz
\end{tabular}


\def\zzz#1{\zzza#1,\relax}
\def\zzza#1,#2\relax{&column #1\ifx\relax#2\relax\else\expandafter\zzza\fi#2\relax}

\begin{tabular}{l*{8}c}
first column \zzz{a,b,c,d,e,f,g,h}
\end{tabular}




\end{document}
0
2

Here is an approach using tokens and the listofitems:

Basically it makes a token list from a comma separated list and then calls the token list when you want it (using \the\tabrows):

\documentclass{article}
\usepackage{listofitems}
\makeatletter
\newtoks\tabrows
% Command for adding tokens to list
\newcommand\addrowraw[1]{\tabrows\expandafter{\the\tabrows#1}}
% Command that takes a comma separated list
% of items to be in the column of a row
% The second argument is what to do with the current column
% using the \thiscol command:
\newcommand\addrow[2]{
  % Macro to contain the new list
  \def\newrow{}
  % Read argument as list for listofitems
  \readlist\thecolumns{#1}
  % Iterate and append the ampersand and the content of each column
  \foreachitem\thiscol\in\thecolumns{
    \edef\newrow{\unexpanded\expandafter{\newrow}&#2}
  }
  % Remove first ampersand
  \edef\newrow{\expandafter\@gobble\newrow}
  % Just grouping it
  \edef\newrow{{\newrow}}
  \expandafter\addrowraw\newrow
  % Add the \cr
  \addrowraw{\\}
}
\begin{document}
  % These should be _before_ the tabular environment
  \addrow{a,b,c,d,e,f,g,h}{Col \thiscol, r1}
  \addrow{i,j,k,l,m,n,o,p}{Col \thiscol, r2}
  \begin{tabular}{l*{8}c}
     \the\tabrows
  \end{tabular}

\end{document}

enter image description here

1
  • Well, thank you. Interesting how you do it with \the\token :)
    – iago-lito
    May 28, 2018 at 8:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .