4

I'd like to create a macro that can create and fill a multiline tabular with 4 entries per line from something like \bidding{3H,4S,5C,X,XX,-} where the number of entries is of course dynamic.

I have tried different ways but I cannot overcome the problem to get latex to interpret the & separator correctly.

I always end up with either the & being not interpreted at all or with the

Misplaced alignment tab character &.

error message. Trying to derive this from another solution found here that does the recursive part, the latest failure (ending up with the error message) looks like this:

\documentclass{article}
\usepackage{kvsetkeys}
\usepackage{etexcmds}
\makeatletter
\newcommand*{\commut}[2]{{#1}&{#2}}
\newcommand*{\qcommut}[1]{%
  \let\commut@list\relax
  \comma@parse{#1}\@qcommut
  \typeout{\meaning\commut@list}% for debugging
  \expandafter\commut@list
}
\def\@qcommut#1{%
  \ifx\commut@list\relax
    \def\commut@list{#1}%
  \else
    \edef\commut@list{%
      \noexpand\commut{%
    \etex@unexpanded\expandafter{\commut@list}%
      }{#1}%
    }%
  \fi
}
\makeatother

\begin{document}
\begin{tabular}{ccccc}
\qcommut{A,B,C,D,E}
\end{tabular}
\end{document}

This will not provide me with the groups of 4 a line, but I am just eager to get the &-problem solved, the rest should be easy... Any help appreciated.

2
  • One should assume that (in your example), you'll never call \qcommut with more elements than there is elements in your tabular column specification? That is, no checking is necessary in that regard.
    – Werner
    May 15, 2014 at 16:30
  • Right. This is just to illustrate my problem. The issue I am unable to solve is that the & separator is not handled as intended. Probably I'll run into other issues later ;)
    – Peter
    May 15, 2014 at 16:34

3 Answers 3

4

I don't understand why such much code is produced for this simple task. I mean that the following code is succesfull.

\documentclass{article}

\def\tabline{}
\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\qcommut#1{\qcommutA#1,,\tabline\cr}
\def\qcommutA#1,{%
   \if^#1^\else
      \ifx\tabline\empty \else\addto\tabline{&}\fi
      \addto\tabline{#1}\expandafter\qcommutA
   \fi
}
\begin{document}
  \begin{tabular}{ccccc}
    \qcommut{A,B,C,D,E}   
    \qcommut{A,B}
    \qcommut{A,B,C}
  \end{tabular}  
\end{document}
2

If the programming environment is not necessarily the one you're used to, here's a way to do it in expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\qtabular}{ O{4} m }
 {
  \peter_qtabular:nn { #1 } { #2 }
 }

\tl_new:N \l__peter_qtabular_body_tl
\seq_new:N \l__peter_qtabular_input_seq
\int_new:N \l__peter_qtabular_cols_int

\cs_new_protected:Npn \peter_qtabular:nn #1 #2
 {
  \tl_clear:N \l__peter_qtabular_body_tl
  \int_zero:N \l__peter_qtabular_cols_int 
  \seq_set_split:Nnn \l__peter_qtabular_input_seq { , } { #2 }
  \seq_map_inline:Nn \l__peter_qtabular_input_seq
   {
    \tl_put_right:Nn \l__peter_qtabular_body_tl { ##1 }
    \int_incr:N \l__peter_qtabular_cols_int
    \int_compare:nTF { \l__peter_qtabular_cols_int = #1 }
     {
      \tl_put_right:Nn \l__peter_qtabular_body_tl { \\ }
      \int_zero:N \l__peter_qtabular_cols_int 
     }
     {
      \tl_put_right:Nn \l__peter_qtabular_body_tl { & }
     }
   }
  \begin{tabular}{*{#1}{c}}
  \tl_use:N \l__peter_qtabular_body_tl
  \end{tabular}
}
\ExplSyntaxOff

\begin{document}
\qtabular{A,B,C,D,E,F,G,H,I,J}

\qtabular[3]{A,B,C,D,E,F,G,H,I,J}
\end{document}

The body of the table is prepared in advance and then delivered in the tabular environment.

enter image description here


A small modification to also fill the last row (with empty cells) in order to be able to use \hline:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\qtabular}{ O{4} m }
 {
  \peter_qtabular:nn { #1 } { #2 }
 }

\tl_new:N \l__peter_qtabular_body_tl
\seq_new:N \l__peter_qtabular_input_seq
\int_new:N \l__peter_qtabular_cols_int

\cs_new_protected:Npn \peter_qtabular:nn #1 #2
 {
  % clear the token list that will contain the body
  \tl_clear:N \l__peter_qtabular_body_tl
  % this counter tells us what column we're at
  \int_zero:N \l__peter_qtabular_cols_int 
  % split the input into items at commas
  \seq_set_split:Nnn \l__peter_qtabular_input_seq { , } { #2 }
  % do a mapping
  \seq_map_inline:Nn \l__peter_qtabular_input_seq
   {% add the current item to the body and increment the counter
    \tl_put_right:Nn \l__peter_qtabular_body_tl { ##1 }
    \int_incr:N \l__peter_qtabular_cols_int
    % if the counter equals the number of columns, add \\, else &
    \int_compare:nTF { \l__peter_qtabular_cols_int = #1 }
     {
      \tl_put_right:Nn \l__peter_qtabular_body_tl { \\ }
      \int_zero:N \l__peter_qtabular_cols_int 
     }
     {
      \tl_put_right:Nn \l__peter_qtabular_body_tl { & }
     }
   }
  % now fill the last row, if necessary; it isn't if the counter is 0
  \int_compare:nF { \l__peter_qtabular_cols_int == 0 }
   {% add as many & as necessary
    \prg_replicate:nn { #1 - \l__peter_qtabular_cols_int - 1 }
     { \tl_put_right:Nn \l__peter_qtabular_body_tl { & } }
    % add the final \\
    \tl_put_right:Nn \l__peter_qtabular_body_tl { \\ }
   }
  % deliver the table
  \begin{tabular}{*{#1}{c}}
  \hline
  \tl_use:N \l__peter_qtabular_body_tl
  \hline
  \end{tabular}
}
\ExplSyntaxOff

\begin{document}
\qtabular{A,B,C,D,E,F,G,H,I,J}

\qtabular[3]{A,B,C,D,E,F,G,H,I,J}

\qtabular[5]{A,B,C,D,E,F,G,H,I,J}
\end{document}

enter image description here

3
  • Cool. Thanks a lot. What is the decicive difference that makes the & "behave" ?
    – Peter
    May 15, 2014 at 17:25
  • @Peter The problem with your macro is that you start a loop in a cell and would like to end it in another which isn't possible, because cells make groups. So these macros don't start cells until the last moment, when the whole table body is delivered at once. I added a slightly modified version with comments.
    – egreg
    May 15, 2014 at 17:45
  • ok, makes sense. Thanks. Impressive how fast this got solved! Great!
    – Peter
    May 15, 2014 at 18:01
2

A method without package.

Update:

  • the macros are made \long to allow fringe cases such as

this

\qtabular[3]{A,
  \begin{minipage}{2cm}
    ABC\par DEF
  \end{minipage},
Z}
  • a new macro \qtabularnewline is defined which allows to insert a \\ at arbitrary locations as well as \hline's or even \noalign material. Achieving the same functionality with an approach based on building up first the entire tabular material could be a bit complicated.

qtabular with qtabularnewline

The code:

\documentclass{article}

\makeatletter

% the update is a bit more clever and has less macros for more effects

\newcommand{\qtabular}[2][4]{% optional argument=nb of columns
  \begin{tabular}{*{#1}{c}}
    \qtabular@a {1}{#1}#2\qtabular@end,%
  \end{tabular}}

% The update makes the macros \long, to be compatible with \par tokens

\long\def\qtabular@a #1#2#3,{#3%
  \expandafter\qtabular@e\expandafter{\the\numexpr #1+1}{#2}}

% Use of \tabularnewline for extra-safety, but quasi unnecessary I guess.
\long\def\qtabular@e #1#2{\ifnum #2<#1
         \expandafter\@firstoftwo
    \else\expandafter\@secondoftwo
    \fi
    {\tabularnewline\qtabular@a {1}{#2}}%
    {&\qtabular@a {#1}{#2}}%
}

\def\qtabular@end\expandafter\qtabular@e\expandafter #1#2{}

\long\def\qtabularnewline #1\expandafter\qtabular@e\expandafter #2#3{%
    \ifnum #2=2 \else \expandafter\tabularnewline\fi
    \qtabular@a {1}{#3}#1,}
% minor remark: if the #1 from what followed \qtabularnewline in the
% input was braced, one level will be removed, but this
% has quasi nil importance, any how the new cell is a group, and #1 will
% occupy it entirely.

\makeatother

\begin{document}

\qtabular[3]{A,
  \begin{minipage}{2cm}
    ABC\par DEF
  \end{minipage},
Z}

\medskip

Using \verb|\fbox|es to check for spurious empty lines:
\medskip

\fbox{\qtabular[3]{\hline A,B,C\qtabularnewline\hline}}
=\fbox{\qtabular[3]{\hline A,B,C,\qtabularnewline\hline}}
=\fbox{\qtabular[3]{\hline A,B,C\\\hline}}
$\neq$\fbox{\qtabular[3]{\hline A,B,C,\\\hline}}

\fbox{\qtabular[3]{\hline A,B,C\qtabularnewline D}}
$\neq$
% a , after \qtabularnewline is like & after \\
\fbox{\qtabular[3]{\hline A,B,C\qtabularnewline, D}}

\fbox{\qtabular[12]{\hline\hline A,B,C\qtabularnewline\hline
                    D,E,F,G,H,I,J,A,B,\qtabularnewline  % a comma
                    % before\qtabularnewline is authorized and non-significant
                    C,D,E,F,\qtabularnewline \hline 
                    G,H,I,J,A,B,C,D,E,F,G,H,I,J\qtabularnewline 
     \noalign{\hbox{DEMONSTRATING THE APPROACH FLEXIBILITY}}
                    A,B,C,D,E,F,G,H,I,A,B,C,D,E,F,G,H,I,J\\\hline\hline}}

\fbox{\qtabular[1]{A,B,C,\qtabularnewline}}% checking it is ok also at
                                % the end
\fbox{\qtabular[1]{\qtabularnewline\hline  A,\qtabularnewline\hline}}
and
\fbox{\qtabular[1]{\hline A\\\hline}}
and
\fbox{\begin{tabular}{c}
  \hline A\\\hline
\end{tabular}}

\end{document}

First version:

qtabular

Code:

\documentclass{article}

\makeatletter

\newcommand{\qtabular}[2][4]{% optional argument=nb of columns
  \begin{tabular}{*{#1}{c}}
    \qtabular@a 1{#1}#2,\qtabular@a,%
  \end{tabular}}

\def\qtabular@a #1#2#3,{%
    \qtabular@b #3\qtabular@c\qtabular@a\qtabular@d
    {#1}{#2}{#3}}

\def\qtabular@b #1\qtabular@a {}

\def\qtabular@c\qtabular@a\qtabular@d #1#2#3{}

\def\qtabular@d #1#2#3{#3%
  \expandafter\qtabular@e\expandafter{\the\numexpr #1+1}{#2}}

\def\qtabular@e #1#2{\ifnum #2<#1
         \expandafter\@firstoftwo
    \else\expandafter\@secondoftwo
    \fi
    {\\\qtabular@a {1}{#2}}%
    {&\qtabular@a {#1}{#2}}%
}

\makeatother

\begin{document}
\hrule
\qtabular{A,B,C,D,E,F,G,H,I,J}
\hrule
\qtabular[3]{A,B,C,D,E,F,G,H,I,J}
\hrule
\qtabular[12]{A,B,C,D,E,F,G,H,I,J,A,B,C,D,E,F,G,H,I,J,A,B,C,D,E,F,G,H,I,J,A,B,C,D,E,F,G,H,I,J,A,B,C,D,E,F,G,H,I,J}
\hrule
\qtabular[5]{A,B,C,D,E,F,G,H,I,J}
\hrule
\qtabular[1]{A} and \qtabular[1]{A,B}
\hrule
\end{document}
3
  • the \hrule are just to check that no spurious empty line arises in a tabular.
    – user4686
    May 15, 2014 at 17:48
  • Integrated this one and it works. Since I want to change a few things but am currently lost in the macro syntax here, can you recommend a page where this is well explained in detail and well. Thanks for writing it up!
    – Peter
    May 15, 2014 at 19:03
  • @Peter this is a method based on the notion of macros with delimited parameters, thus its logic is quite peculiar to TeX; macros with parameters are explained in TeX by Topic ("texdoc topic"). The method, to avoid making definitions or using registers, uses \numexpr which is not covered in TeX by Topic, as it is a very recent thing in the tex world (late nineties ;-) ). Thus, I am a bit at a loss for recommending a suitable page, the e-TeX manual ("texdoc etex") is very terse!
    – user4686
    May 15, 2014 at 21:17

You must log in to answer this question.

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