How do I use the ampersand (&) inside a foreach or conditional (or other group/environment) when building tables?

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


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}

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}