At the bottom of every section in a big policy manual I need to include a table of references to a procedures manual like this:

\begin{table}[H]
\begin{tabular}{ |c|c| } 
  \hline
  \multicolumn{2}{|c|}{Procedures} \\
  \hline
  %fyi PM = Procedures Manual
  PM \ref{PM-tyinglaces} & \nameref{PM-tyinglaces} \\ 
  PM \ref{PM-polishshoes & \nameref{PM-polishshoes} \\
  PM \ref{PM-ironshirt} & \nameref{PM-ironshirt} \\
  \hline
\end{tabular}
\end{table}

Of course since every table is formatted the same I'd really like a macro with syntax similar to:

%\procedurelist{firstlabel}{nextlabel}...{nthlabel}
%
\procedurelist{PM-tyinglaces}{PM-polishshoes}{PM-ironshirt}

A single comma deliminated parameter would be okay too. My attempt is found below. I just can't get it to close the table, somehow it breaks when I include the closing \hline.

\makeatletter
\newcommand{\procedurelist}[1]{%
    \begin{table}[H]
    \begin{tabular}{ |c|c| } 
    \hline
    \multicolumn{2}{|c|}{Procedures} \\
    \hline
    PM \ref{#1} & \nameref{#1} \\%
    \checknextarg
}
\newcommand{\checknextarg}{%
    \@ifnextchar\bgroup{%
        \consumenextarg
    }{%
        \hline  %causes error?
        \end{tabular}
        \end{table}
    }
}
\newcommand{\consumenextarg}[1]{%
    PM \ref{#1} & \nameref{#1} \\%
    \@ifnextchar\bgroup{%
        \consumenextarg
    }{%
        \hline %causes error?
        \end{tabular}
        \end{table}
    }
}
\makeatother

How do I achieve my goal?

What am I doing wrong in my macro?

  • The error of the \hline is caused because you used \checknextarg which isn't expandable as it uses \@ifnextchar which is not expandable. Then you want to issue a \noalign which is not the first token in that row. – Skillmon Sep 18 at 19:33
up vote 3 down vote accepted

I would not recommend using a syntax such as

\procedurelist{a}{b}{c}...{z}

and much prefer something like

\procedurelist{a,b,c,...,z}

It is not really difficult to do the way you imagined. The trick is to build the table body in advance.

\documentclass{article}
\usepackage{hyperref}

\makeatletter
\newcommand{\procedurelist}{%
  \proclist@body={}%
  \proclist@start
}

\newtoks\proclist@body
\newcommand{\proclist@start}{%
  \@ifnextchar\bgroup\proclist@absorb\proclist@finish
}
\newcommand{\proclist@absorb}[1]{%
  \proclist@body=\expandafter{%
    \the\proclist@body
    PM~\ref{#1} & \nameref{#1} \\%
  }%
  \proclist@start
}
\newcommand{\proclist@finish}{%
  \begin{tabular}{|c|c|}
  \hline
  \the\proclist@body
  \hline
  \end{tabular}%
}
\makeatother

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces}{PM-polishshoes}{PM-ironshirt}

\end{document}

The other approach with a comma separated list is easier with expl3, which already has features for processing comma separated lists and cycling over them.

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{m}
 {
  \seq_clear:N \l__kjc_procedurelist_seq
  \clist_map_inline:nn { #1 }
   {
    \seq_put_right:Nn \l__kjc_procedurelist_seq
     {
      PM~\ref{##1} & \nameref{##1}
     }
   }
  \begin{tabular}{|c|c|}
  \hline
  \seq_use:Nn \l__kjc_procedurelist_seq { \\ } \\
  \hline
  \end{tabular}
 }

\seq_new:N \l__kjc_procedurelist_seq

\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}  

\end{document}

enter image description here

A better table:

\documentclass{article}
\usepackage{xparse,booktabs}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{m}
 {
  \seq_clear:N \l__kjc_procedurelist_seq
  \clist_map_inline:nn { #1 }
   {
    \seq_put_right:Nn \l__kjc_procedurelist_seq
     {
      \ref{##1} & \nameref{##1}
     }
   }
  \begin{tabular}{cl}
  \toprule
  \multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
  \midrule
  \seq_use:Nn \l__kjc_procedurelist_seq { \\ } \\
  \bottomrule
  \end{tabular}
 }

\seq_new:N \l__kjc_procedurelist_seq

\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}  

\end{document}

enter image description here

A simpler implementation, once one gets the hang of it:

\documentclass{article}
\usepackage{xparse,booktabs}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{m}
 {
  \cs_set:Nn \__kjc_procedurelist_row:n { \ref{##1} & \nameref{##1} \\ }
  \begin{tabular}{cl}
  \toprule
  \multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
  \midrule
  \clist_map_function:nN { #1 } \__kjc_procedurelist_row:n
  \bottomrule
  \end{tabular}
 }

\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}  

\end{document}

With this approach the \__kjc_procedurelist_row:n function could be defined outside the main command. However one can also improve this by allowing to change the default on the fly:

\documentclass{article}
\usepackage{xparse,booktabs}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\procedurelist}{om}
 {
  \IfValueT{#1}
   {
    \cs_set:Nn \__kjc_procedurelist_row:n { #1 }
   }
  \begin{tabular}{cl}
  \toprule
  \multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
  \midrule
  \clist_map_function:nN { #2 } \__kjc_procedurelist_row:n
  \bottomrule
  \end{tabular}
 }
\cs_new:Nn \__kjc_procedurelist_row:n { \ref{#1} & \nameref{#1} \\ }
\ExplSyntaxOff

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{
  PM-tyinglaces,
  PM-polishshoes,
  PM-ironshirt
}
\procedurelist[\ref{#1} & \textit{\nameref{#1}} \\]{
  PM-tyinglaces,
  PM-polishshoes,
  PM-ironshirt
}

\end{document}

enter image description here

  • Why do you define \__kjc_procedurelist_tmp:n every time \procedurelist is called in your last implementation? Should be better performing if set only once (assuming \procedurelist is called more than once). – Skillmon Sep 18 at 22:05
  • @Skillmon Sure. Let me add something. – egreg Sep 18 at 22:32

The trick is to first grab all those arguments and store them somewhere (here in \procedurelist@content). After you grabbed all arguments you can then output the formatted table. This way you don't have any unexpandable tests inside of the table blocking any \noaligns.

Example code (I removed the \namerefs and the surrounding table as that doesn't make sense if the only allowed placement is H and you don't use a caption):

\documentclass[]{article}

\makeatletter
\newcommand*\procedurelist@content{}
\newcommand*\procedurelist@checknext
  {%
    \@ifnextchar\bgroup
      {\procedurelist@eatnext}
      {\procedurelist@shipout}%
  }
\newcommand\procedurelist[1]
  {%
    \begingroup
    \def\procedurelist@content{PM \ref{#1} & #1 \\}%
    \procedurelist@checknext
  }
\newcommand\procedurelist@eatnext[1]
  {%
    \edef\procedurelist@content
      {\unexpanded\expandafter{\procedurelist@content PM \ref{#1} & #1 \\}}%
    \procedurelist@checknext
  }
\newcommand*\procedurelist@shipout
  {%
    \begin{tabular}{|c|c|}
      \hline
      \multicolumn{2}{|c|}{Procedures} \\
      \hline
      \procedurelist@content
      \hline
    \end{tabular}%
    \endgroup
  }
\makeatother

\begin{document}
\procedurelist{arg1}{arg2}{arg3}{arg4}
\end{document}

enter image description here

Using \xintFor (which is not expandable loop, but nevertheless works in tabulars...)

\documentclass{article}
\usepackage{booktabs}
\usepackage{hyperref}
\usepackage{xinttools}

\newcommand{\procedurelist}[1]{%
  \begin{tabular}{cl}
  \toprule
  \multicolumn{1}{c}{\textbf{PM}} & \textbf{Name} \\
  \midrule
  \xintFor ##1 in {#1}\do {\ref{##1} & \nameref{##1} \\ }
  \bottomrule
  \end{tabular}
}

\begin{document}

\section{Tie laces}\label{PM-tyinglaces}
\section{Polish shoes}\label{PM-polishshoes}
\section{Iron shirt}\label{PM-ironshirt}

\section{End}

\procedurelist{PM-tyinglaces,PM-polishshoes,PM-ironshirt}  

\end{document}

enter image description here

  • With mark-up such as \procedurelist{{PM-tyinglaces}{PM-polishshoes}{PM-ironshirt}}, same code as in my answer with \xintFor ##1 in {#1} replaced by \xintFor* #1 in {##1}. (slightly more efficient as any way \xintFor converts the comma separated values into the braced values expected by \xintFor*; but with only 3 values...) – jfbu Sep 18 at 21:21

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.