18

I am trying to build a table, and inside the tabularx environment I would like to have a loop, something like (here is a MWE):

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\newcommand{\nRows}{5}
\begin{document}


\begin{tabularx}{\textwidth}{@{}>{\raggedright}p{2.2cm}@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}}
    \foreach \r in {1,...,\nRows}{  % https://stackoverflow.com/questions/2561791/iteration-in-latex
        \textbf{column1} & two & three & four & five 
    }
\end{tabularx}

\end{document}

This doesn't work, because the ampersands (&) are causing problems. If I escape the &s, then the entire line gets printed in the first column, along with the literal ampersands, kind of like:

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\newcommand{\nRows}{5}
\begin{document}


\begin{tabularx}{\textwidth}{@{}>{\raggedright}p{2.2cm}@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}}
    \foreach \r in {1,...,\nRows}{  % https://stackoverflow.com/questions/2561791/iteration-in-latex
        \textbf{column1} \& two \& three \& four \& five 
    }
\end{tabularx}

\end{document}

This yields:

|column1 &      | | | |
| two & three & | | | |
| four & five   | | | |

Whereas what I want is:

|column 1 | two | three | four | five |
|column 1 | two | three | four | five |
|column 1 | two | three | four | five |

....

Any ideas on how I can achieve this? I'm actually NOT looking to make identical rows - the rows will be different. This is just a MWE. Thanks!

Edit: I have tried implementing Herbert's answer as recommended in the comments section. It results in no output - just a blank PDF. Here is how I implemented it:

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\newcommand{\nRows}{5}

\makeatletter
\newtoks\@tabtoks
\newcommand\addtabtoks[1]{\@tabtoks\expandafter{\the\@tabtoks#1}}
\newcommand*\resettabtoks{\@tabtoks{}}
\newcommand*\printtabtoks{\the\@tabtoks}
\makeatother

\begin{document}

    \resettabtoks
    \foreach \r in {1,...,\nRows}{  % https://stackoverflow.com/questions/2561791/iteration-in-latex
        % \textbf{column1} \& two \& three \& four \& five 
        \addtabtoks{\textbf{column1} & two & three & four & five \\\hline}%
    }

\begin{tabularx}{\textwidth}{@{}>{\raggedright}p{2.2cm}@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}}
\printtabtoks
\end{tabularx}

\end{document}

Any ideas on what I am doing wrong? Thanks!

7
  • 1
    Herbert's answer at tex.stackexchange.com/questions/7590/… is very much related. Commented Mar 12, 2014 at 14:39
  • Welcome to TeX.SX! Please help us to help you and add a minimal working example (MWE) that illustrates your problem. It will be much easier for us to reproduce your situation and find out what the issue is when we see compilable code, starting with \documentclass{...} and ending with \end{document}.
    – user31729
    Commented Mar 12, 2014 at 14:59
  • 2
    Also you need \raggedright\arraybackslash in your preamble. Commented Mar 12, 2014 at 16:43
  • 2
    You have to use \global\@tabtoks, because \foreach executes each cycle in a group. Related: tex.stackexchange.com/questions/62177/copy-table-row-n-times
    – egreg
    Commented Mar 12, 2014 at 16:44
  • 1
    You are also missing \arraybackslash after \raggedright in the last column specification.
    – egreg
    Commented Mar 12, 2014 at 16:51

3 Answers 3

13

Presumably you are doing this because you want the rows of the table to depend on the counter in the loop. In this case you will need a way to add expanded objects to the list of tokens. Here I add an \eaddtabtoks macro in addition to egreg's corrections to the global assignments.

Sample output

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx,etoolbox}
\usepackage{tikz}
\newcommand{\nRows}{5}

\makeatletter
\newtoks\@tabtoks
\newcommand\addtabtoks[1]{\global\@tabtoks\expandafter{\the\@tabtoks#1}}
\newcommand\eaddtabtoks[1]{\edef\mytmp{#1}\expandafter\addtabtoks\expandafter{\mytmp}}
\newcommand*\resettabtoks{\global\@tabtoks{}}
\newcommand*\printtabtoks{\the\@tabtoks}
\makeatother

\begin{document}

\resettabtoks
\foreach \r in {1,...,\nRows}{
  \addtabtoks{\textbf{column 1} &}
  \eaddtabtoks{row \r}
  \addtabtoks{ & three & four & five \\}%
}

\begin{tabularx}{\textwidth}{@{}>{\raggedright\arraybackslash}p{2.2cm}*{4}{@{}>{\raggedright\arraybackslash}X}@{}}
\printtabtoks
\end{tabularx}%

\end{document}
3
  • Is there any way one could use \cellcolor{...} within \eaddtabtoks{...}? I'd like to get a conditional coloring of cells. Everything works alright, but when I add \cellcolor get various errors, like: Use of \@@array doesn't match its definition ... Commented Jun 7, 2016 at 20:36
  • @thewaywewalk You might get away with inserting \noexpand before \cellcolor. If that doesn't work, ask a new question, showing the code you have. Commented Jun 8, 2016 at 7:32
  • It seems to work, thanks for the advice! I finally got away with another looping structure yesterday. Commented Jun 8, 2016 at 9:48
14

You're missing the fact that \foreach executes each cycle in a group, so when finishing it the change to \@tabtoks is undone.

Add \global in the relevant places (and also \arraybackslash in the specification for the last column, but this is another problem).

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\newcommand{\nRows}{5}

\makeatletter
\newtoks\@tabtoks
%%% assignments to \@tabtoks must be global, because they are done in \foreach
\newcommand\addtabtoks[1]{\global\@tabtoks\expandafter{\the\@tabtoks#1}}
%%% variable should always be operated on always locally or always globally
\newcommand*\resettabtoks{\global\@tabtoks{}}
\newcommand*\printtabtoks{\the\@tabtoks}
\makeatother

\begin{document}
\resettabtoks
\foreach \r in {1,...,\nRows}{
  \addtabtoks{\textbf{column1} & two & three & four & five \\\hline}%
}
\noindent\begin{tabularx}{\textwidth}{
  @{}>{\raggedright}p{2.2cm}
  @{}>{\raggedright}X
  @{}>{\raggedright}X
  @{}>{\raggedright}X
  @{}>{\raggedright\arraybackslash}X
  @{}
}
\printtabtoks
\end{tabularx}

\end{document}

A more complex example

Suppose we want each table row to depend on the loop index; then some change has to be made. The new \addtabtoks macro takes an optional argument that, if expressed, should be the command representing the index in the \foreach loop; the mandatory argument, in this case, should be a one parameter macro, like in the code below; the parameter should be the loop index.

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\newcommand{\nRows}{5}

\makeatletter
\newtoks\@tabtoks
%%% assignments to \@tabtoks must be global, because they are done in \foreach
\newcommand\addtabtoks[2][]{%
  \if\relax\detokenize{#1}\relax
    % no index, just append the second argument
    \global\@tabtoks\expandafter{\the\@tabtoks#2}%
  \else
    % we assume the second argument is a one parameter macro
    \global\@tabtoks\expandafter{\the\expandafter\@tabtoks\expandafter#2\expandafter{#1}}%
  \fi
}
%%% variable should always be operated on always locally or always globally
\newcommand*\resettabtoks{\global\@tabtoks{}}
\newcommand*\printtabtoks{\the\@tabtoks}
\makeatother

% define a one parameter macro for making the row depending on the current index
\newcommand{\tablerow}[1]{%
  \textbf{Row #1} & two & three & four & five \\\hline
}


\begin{document}
\resettabtoks
\foreach \r in {1,...,\nRows}{%
  \addtabtoks{\textbf{column1} & two & three & four & five \\\hline}%
}
\noindent\begin{tabularx}{\textwidth}{
  @{}>{\raggedright}p{2.2cm}
  @{}>{\raggedright}X
  @{}>{\raggedright}X
  @{}>{\raggedright}X
  @{}>{\raggedright\arraybackslash}X
  @{}
}
\hline
\printtabtoks
\end{tabularx}

\bigskip

\resettabtoks
\foreach \r in {1,...,\nRows}{%
  \addtabtoks[\r]{\tablerow}%
}
\noindent\begin{tabularx}{\textwidth}{
  @{}>{\raggedright}p{2.2cm}
  @{}>{\raggedright}X
  @{}>{\raggedright}X
  @{}>{\raggedright}X
  @{}>{\raggedright\arraybackslash}X
  @{}
}
\hline
\printtabtoks
\end{tabularx}

\end{document}

enter image description here

1
8

If your are open to using (here) another loop than \foreach then there are options, like \xintFor from package xinttools. The syntax is a bit different, and using \xintFor at this place does not exclude using \foreach at others...

Update adds a second method for people loving the bleeding edge.

tabularx loop

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\usepackage{xinttools}
\newcommand{\nRows}{5}
% \usepackage{tabu} % alternative to tabularx
\begin{document}

\begin{tabularx}{\textwidth}{@{}>{\raggedright}p{2.2cm}@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright\arraybackslash}X@{}}
    \xintFor* #1 in {\xintSeq {1}{\nRows}}
    \do
    {(\textsl{row #1}) \textbf{column1} & two & three & four & five
    \\ }
\end{tabularx}


% Note with a tabu environment of package tabu, \arraybackslash is not needed
% \begin{tabu} to \textwidth{@{}>{\raggedright}p{2.2cm}@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}}
%     \xintFor* #1 in {\xintSeq {1}{\nRows}}
%     \do
%     {(\textsl{row #1}) \textbf{column1} & two & three & four & five
%     \\ }
% \end{tabu}

\end{document}

Here is an idiosyncratic way which relies on an expandable loop (\xintFor is not expandable despite the fact it enjoys tabulars), and without counters! (\xintFor does not use counters either). \xintiloopindex can not be within braces; and in this example we need to hide the tabulations, for the \xintiloopindex mechanism to work, because non-expandable material is encountered before.

\documentclass[10pt,a4paper]{article}
\usepackage{tabularx}
\usepackage{tikz}
\usepackage{xinttools}
\newcommand{\nRows}{5}
\def\TAB{&}
\begin{document}

\begin{tabularx}{\textwidth}{@{}>{\raggedright}p{2.2cm}@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright}X@{}>{\raggedright\arraybackslash}X@{}}
    \xintiloop [1+1]
    (\bgroup\slshape row \xintiloopindex\egroup )% \xintiloopindex can not be
                                % within braces...
            \textbf{column1} \TAB two \TAB three \TAB four \TAB five \\
    \ifnum\nRows>\xintiloopindex\space
    \repeat
\end{tabularx}

\end{document}

You must log in to answer this question.

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