Since your goal seems to be to reduce redundancy, I suggest using something like this, or the more flexible variant given below:
\RequirePackage{filecontents}
\begin{filecontents*}{data.csv}
position
first
second
third
fourth
\end{filecontents*}
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{datatool}
\colorlet{OddRowColor}{orange}%
\colorlet{EvenRowColor}{gray!20}%
\DTLloaddb{rows}{data.csv}
\begin{document}
\begin{tabular}{c}
\rowcolor{OddRowColor}%
\DTLforeach*{rows}{\word=position}{%
\word
\\ % ending the row, \noalign can thus be used here
\DTLiflastrow{}{% This is the color for the *next* row, hence the odd/even
% shift compared to what datatool just processed
\DTLifoddrow{\rowcolor{EvenRowColor}}{\rowcolor{OddRowColor}}%
}%
}
\end{tabular}
\end{document}
Note regarding filecontents
: if your LaTeX2e release is from October 2019 or more recent, it has features that render the filecontents
package obsolete. In this case, you can replace the part preceding \documentclass
with the following (see here for details, in particular the LaTeX2e News Issue 30):
\begin{filecontents}[noheader,overwrite]{data.csv}
position
first
second
third
fourth
\end{filecontents}
Back to our document. If you find the markup too verbose in the tabular
, you can define a macro like this:
\newcommand*{\myApplyAlternatingColorsForTable}{%
\DTLiflastrow{}{% This is the color for the *next* row, hence the odd/even
% shift compared to what datatool just processed
\DTLifoddrow{\rowcolor{EvenRowColor}}{\rowcolor{OddRowColor}}%
}%
}
Then your tabular can look like:
\begin{tabular}{c}
\rowcolor{OddRowColor}%
\DTLforeach*{rows}{\word=position}{%
\word
\\ % ending the row, \noalign can thus be used here
\myApplyAlternatingColorsForTable
}
\end{tabular}
It's tempting to make \myApplyAlternatingColorsForTable
accept the two colors as parameters, but this would miss the first tabular row which also needs its color to be given. So, to get one step further, you would need to define macros before the table that expand to the desired colors, like this:
\RequirePackage{filecontents}
\begin{filecontents*}{data.csv}
position
first
second
third
fourth
\end{filecontents*}
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{datatool}
\newcommand*{\myApplyAlternatingColorsForTable}{%
\DTLiflastrow{}{% This is the color for the *next* row, hence the odd/even
% shift compared to what datatool just processed
\DTLifoddrow{\rowcolor{\myColorForEvenRows}}%
{\rowcolor{\myColorForOddRows}}%
}%
}
\newcommand*{\mySetColorsForNextTable}[2]{%
\def\myColorForOddRows{#1}%
\def\myColorForEvenRows{#2}%
}
\newcommand*{\myApplyColorToFirstRow}{%
\rowcolor{\myColorForOddRows}%
}
\DTLloaddb{rows}{data.csv}
\begin{document}
\mySetColorsForNextTable{orange}{gray!20}%
\begin{tabular}{c}
\myApplyColorToFirstRow
\DTLforeach*{rows}{\word=position}{%
\word
\\ % ending the row, \noalign can thus be used here
\myApplyAlternatingColorsForTable
}
\end{tabular}
\end{document}
What you probably wanted to do is this:
\begin{tabular}{c}
\DTLforeach*{rows}{\word=position}{%
\DTLifoddrow{\rowcolor{orange}}{\rowcolor{gray}}%
\word
\\
}
\end{tabular}
This doesn't work because of the way \rowcolor
and TeX alignments such as \halign
(like the tabular
environment) work. \rowcolor
uses a TeX primitive called \noalign
, which can only appear at the beginning of the first line or after a \cr
or \crcr
(which is used internally by tabular
to end a tabular row, when you use \\
). When looking for \noalign
or \omit
(which is special in the same respect as \noalign
for this discussion), TeX expands tokens and stops at the first non-expandable, non-space token (cf. TeXbook p. 240).
When using \rowcolor
at the beginning or end of a line, the first non-expandable, non-space token found is \noalign
, therefore it works fine. But when using something even simpler than the above non-working code:
\begin{tabular}{c}
\DTLforeach*{rows}{\word=position}{%
\rowcolor{blue}%
\word
\\
}
\end{tabular}
you get an error like so:
./thefile.tex:23: Misplaced \noalign.
\rowcolor ->\noalign
{\ifnum 0=`}\fi \global \let \CT@do@color \CT@@[email protected] }
This is because \DTLforeach
is expanded as part of the aforementioned process during which TeX expands tokens looking for \noalign
or \omit
, and this stops at a non-expandable, non-space token way before \rowcolor
got a chance to be expanded and yield its \noalign
. Indeed, in order to check if its star form is used, \DTLforeach
starts by calling \new@ifnextchar
, which contains a \let
in its expansion, and \let
is precisely a non-expandable, non-space token. At this point, using \noalign
before the next \cr
or \crcr
,1 becomes invalid. TeX then processes the start template of the next column (what you can define with >{...}
when using the array
package), then the entry contents before getting to the column's end template. While processing the entry contents, TeX expands the \rowcolor
macro following \DTLforeach*{rows}{\word=position}{
, and this expansion yields the \noalign
we already mentioned. Unfortunately, it is too late for \noalign
to be valid here, because of the \let
obtained from the expansion of \DTLforeach
. \noalign
will only be valid to see again after the next \cr
or \crcr
.
Are \DTLiflastrow
and \DTLifoddrow
safe?
You may wonder why \DTLiflastrow
and \DTLifoddrow
appear to work in our example, where \DTLforeach*
doesn't. Well... because you've been lucky! Update: we are explaining a bug present in datatool 2018/12/07 v2.31; in the meantime, it has been fixed in datatool
2019/09/27 v2.32.
Look at this definition:
\gdef\DTLiflastrow##1##2{%
\expandafter\ifnum
\csname c@DTLrow\romannumeral\dtlforeachlevel\endcsname
=\csname dtlrows@#2\endcsname\relax
##1%
\else
##2%
\fi}%
There was a little bug (or, let's say, undesirable behavior): it works better without the \relax
. In order to trigger the bug (only present in datatool
< 2.32), try to use \rowcolor
in the 〈true〉 clause of \DTLiflastrow, you'll get a “Misplaced \noalign
” error, just as we explained. So, with “old” datatool
(e.g., 2018/12/07 v2.31), \DTLiflastrow
can only contain \noalign
material in the 〈false〉 clause. You weren't hit by the bug because TeX skips over the \relax
without seeing it when the \DTLiflastrow
test is false (i.e., when it is looking for the \else
token), and you were only using \noalign
material in the 〈false〉 clause. But the problem is easy to correct, and the fix is now in datatool
2019/09/27 v2.32: since \dtlrows@DBNAME
is a \countdef
token, it is a 〈number〉 according to TeX's grammar, therefore the \relax
wasn't needed. If one removes the \relax
(beware, there are two places that do \gdef\DTLiflastrow
in datatool.sty, one for \DTLforeach
and one for \DTLforeach*
), then one can manually append, say, a green line containing abc
to the tabular, like this:
\RequirePackage{filecontents}
\begin{filecontents*}{data.csv}
position
first
second
third
fourth
\end{filecontents*}
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{datatool}
\newcommand*{\myApplyAlternatingColorsForTable}{%
\DTLiflastrow{\rowcolor{green}}{%
\DTLifoddrow{\rowcolor{\myColorForEvenRows}}%
{\rowcolor{\myColorForOddRows}}%
}%
}
\newcommand*{\mySetColorsForNextTable}[2]{%
\def\myColorForOddRows{#1}%
\def\myColorForEvenRows{#2}%
}
\newcommand*{\myApplyColorToFirstRow}{%
\rowcolor{\myColorForOddRows}%
}
\DTLloaddb{rows}{data.csv}
\begin{document}
\mySetColorsForNextTable{orange}{gray!20}%
\begin{tabular}{c}
\myApplyColorToFirstRow
\DTLforeach*{rows}{\word=position}{%
\word
\\ % ending the row, \noalign can thus be used here
\myApplyAlternatingColorsForTable
}
abc
\end{tabular}
\end{document}
\DTLifoddrow
didn't have this bug (we would have noticed since we used both its 〈true〉 and 〈false〉 clauses), but \DTLiffirstrow
had it for both \DTLforeach
and \DTLforeach*
in datatool
< 2.32. All this has been fixed in datatool
2019/09/27 v2.32. The above example works out of the box with that version.
Footnote
- End-of-line primitives for TeX alignments (
\halign
and \valign
), internally used by tabular
, array
and friends.