0

I try to obtain alternate colored rows (just like the table is shown in the figure) using the datatool package and the \rowcolor command. I am following the datatool user manual example for striped tables, but it is not enterely clear. The issue for me is how to color the first row, because the manual does not really deal with that.

It looks like \rowcolor, \DTLifoddrow, \DTLiffirstrow and \DTLiflastrow has themselves certain rules which I can't understand fully.

Is there a more compact way to do this? I mean, why it is not possible to call all the \rowcolor statements inside the loop?

\documentclass{article}

\usepackage[table]{xcolor}
\usepackage{datatool}

\begin{filecontents*}{data.csv}
position
first
second
third
fourth
\end{filecontents*}

\DTLloaddb{rows}{data.csv}

\begin{document}

\begin{tabular}{c}
\hline
\rowcolor{orange} % <- redundant? really needed?
\DTLforeach*{rows}{\word=position}{
    \word%
    \DTLiflastrow{}{\\\DTLifoddrow{\rowcolor{gray}}{\rowcolor{orange}}}%
    }
\\ \hline

\end{tabular}

\end{document}

enter image description here

1

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}

Screenshot

If you find it 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@@do@color...l.23 }

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!

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 is a little bug (or, let's say, undesirable behavior): the \relax should be removed, in my opinion. In order to trigger the bug, try to use \rowcolor in the 〈true〉 clause of \DTLiflastrow, you'll get a “Misplaced \noalign” error, just as we explained. So, in my version of datatool.sty (2018/12/07 v2.31 (NLCT)), \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 it is easy to fix: since \dtlrows@DBNAME is a \countdef token, it is a 〈number〉 according to TeX's grammar, therefore the \relax isn't needed. If you remove the \relax (beware, there are two places that do \gdef\DTLiflastrow in datatool.sty, one for \DTLforeach and one for \DTLforeach*, and both have the bug), then you can manually append, say, a green line containing abc to your 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}

Screenshot

\DTLifoddrow doesn't have this bug (we would have noticed since we used both its 〈true〉 and 〈false〉 clauses), but other tests have it: at least \DTLiffirstrow has the same bug for both \DTLforeach and \DTLforeach* in my version of datatool.sty.


Footnote

  1. End-of-line primitives for TeX alignments (\halign and \valign), internally used by tabular, array and friends.
  • Maybe I was no clear. I want no headers. I just would to know why it is not possible to call all the \rowcolor statements inside the loop. – e_moro May 24 at 9:27
  • This is because \rowcolor uses \noalign and thus can only be called in specific places, I'll explain that in the answer. – frougon May 24 at 9:44
  • I have updated my answer with detailed explanations and uncovered a bug in \DTLiflastrow when using \noalign material such as \rowcolor in the 〈true〉 clause (\DTLiffirstrow has the same bug, maybe also other tests). I've explained how to fix the bug, but of course, it should be reported to the package author (my version of datatool.sty is the last to this date). – frougon May 24 at 14:03
  • I have submitted a report on datatool's bug tracker. According to Nicola's site, it may need a few days to appear because of spam filtering measures. – frougon May 24 at 15:08
  • Excellent explanation. I understand the main ideas, despite my limited knowledge of low level TeX (I'm just a curious end user of LaTeX). Congratulations for finding the bug! – e_moro May 27 at 7:38

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.