[Tex/LaTex] Macro for creating a table

automationenvironmentsmacrostablestabularx

I want to have a macro that creates tables of a specific type which specialize in multiple choices questions.

The required command \createtable{#1}{#2}{#3} has three arguments:

  1. The total number of questions
  2. The number of columns in each row
  3. The caption of table

It also should create a label for that table.

Examples are given below:

\documentclass{article}

\usepackage[table,xcdraw]{xcolor}
\usepackage{tabu}

\begin{document}

\section{createtable[20][10][table 1 caption]}
\begin{table}[h]
\centering
\caption{table 1 caption}
\label{table:1}   %%% automatically generate {table:\tableCounter} as label

\begin{tabu} to 0.8\textwidth {| *{10}{X[c]|}}

\hline
\rowcolor[HTML]{EFEFEF}
1  & 2  & 3  & 4  & 5  & 6  & 7  & 8  & 9  & 10 \\ \hline
   &    &    &    &    &    &    &    &    &    \\ [1ex]\hline
\rowcolor[HTML]{EFEFEF}
11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\ \hline
   &    &    &    &    &    &    &    &    &    \\ [1ex]\hline

\end{tabu}
\end{table}

\section{createtable[17][10][table 2 caption]}
\begin{table}[h]
\centering
\caption{table 2 caption}
\label{table:2}   %%% automatically generate {table:\tableCounter} as label
\begin{tabu} to 0.8\textwidth {| *{10}{X[c]|}}
\hline
\rowcolor[HTML]{EFEFEF}
1  & 2  & 3  & 4  & 5  & 6  & 7  & 8                        & 9                        & 10                       \\ \hline
   &    &    &    &    &    &    &                          &                          &                          \\ [1ex]\hline
\rowcolor[HTML]{EFEFEF}
11 & 12 & 13 & 14 & 15 & 16 & 17 &                          &                          &                          \\ \hline
   &    &    &    &    &    &    & \cellcolor[HTML]{9B9B9B} & \cellcolor[HTML]{9B9B9B} & \cellcolor[HTML]{9B9B9B} \\ [1ex]\hline
\end{tabu}
\end{table}

\section{createtable[17][6][table 3 caption]}
\begin{table}[h]
\centering
\caption{table 3 caption}
\label{table:3}  %%% automatically generate {table:\tableCounter} as label

\begin{tabu} to 0.8\textwidth {| *{6}{X[c]|}}

\hline
\rowcolor[HTML]{EFEFEF}
1  & 2  & 3  & 4  & 5  & 6  \\ \hline
   &    &    &    &    &    \\ [1ex]\hline
\rowcolor[HTML]{EFEFEF}
7  & 8  & 9  & 10 & 11 & 12 \\ \hline
   &    &    &    &    &    \\ [1ex]\hline
\rowcolor[HTML]{EFEFEF}
13 & 14 & 15 & 16 & 17 &    \\ \hline
   &    &    &    &    & \cellcolor[HTML]{9B9B9B}    \\ [1ex]\hline
\end{tabu}
\end{table}

\end{document} 

enter image description here

How can I do this?

Best Answer

Using the powerful tikz package, we can define a \newcommand with three arguments as follows:

\createtable{<# Questions>}{<# Columns>}{<Table caption>}

like this:

\documentclass[12pt,a4paper]{article}
\usepackage{tikz,xcolor}
\usetikzlibrary{calc}
\begin{document}

\newcounter{xy}
\definecolor{Qcolor}{HTML}{EFEFEF}
\definecolor{noQcolor}{HTML}{9B9B9B}
\newcommand{\createtable}[3]{%
\begin{table}[!h]\centering
\pgfmathparse{ceil(#1/#2)} 
\edef\yfin{\pgfmathresult}
\setcounter{xy}{0}
\begin{tikzpicture}[cell/.style={draw,minimum size=1cm}]
\foreach \y [count=\yi] in {1,...,\yfin}{%
   \foreach \x [count=\xi] in {1,...,#2}{%
   \addtocounter{xy}{1}
   \node [cell,fill=Qcolor]at ([shift={(0,-\yi)}]\xi,-\yi) {%
   \ifnum\the\value{xy}>#1{}\else\the\value{xy}\fi};
   \node [cell,fill={\ifnum\the\value{xy}>#1noQcolor\else white\fi}] 
   at ([shift={(0,-\yi-1)}]\xi,-\yi) {};}}
\end{tikzpicture}
\caption{#3} \label{Tab:#1#2}
\end{table}
}
\createtable{20}{10}{Table 1 caption} 
\createtable{17}{10}{Table 2 caption}
\createtable{17}{6}{Table 3 caption}
We can refer to Table \ref{Tab:2010}, Table \ref{Tab:1710} or Table \ref{Tab:176} as usual.
\end{document}

enter image description here

If the table is required to occupy the total \linewidth, we can modify the width of the cell to be equal to \linewidth/# columns as follows:

\newcounter{xy}
\definecolor{Qcolor}{HTML}{EFEFEF}
\definecolor{noQcolor}{HTML}{9B9B9B}
\newcommand{\createtable}[3]{%
\begin{table}[!h]\centering
\pgfmathparse{ceil(#1/#2)} 
\edef\yfin{\pgfmathresult}
\setcounter{xy}{0}
\begin{tikzpicture}[cell/.style={draw,minimum height=1cm,minimum width=\linewidth/#2}]
\foreach \y [count=\yi] in {1,...,\yfin}{%
   \foreach \x [count=\xi] in {1,...,#2}{%
   \addtocounter{xy}{1}
   \node [cell,fill=Qcolor]at ([shift={(0,-\yi)}]\xi*\linewidth/#2,-\yi) {%
   \ifnum\the\value{xy}>#1{}\else\the\value{xy}\fi};
   \node [cell,fill={\ifnum\the\value{xy}>#1noQcolor\else white\fi}] 
   at ([shift={(0,-\yi-1)}]\xi*\linewidth/#2,-\yi) {};}}
\end{tikzpicture}
\caption{#3} \label{Tab:#1#2}
\end{table}
}

enter image description here

Details

PGF has a good mathematical engine bywhich we can evaluate many expressions and perform many mathematical functions, besides a for/foreach loop for repeating actions. Since the required table is mainly a Question number with an empty cell below it in an ordered manner, then using a for loop is a natural choice. Also, it is so easy to fill a node with some color in TiKZ.

I used \pgfmathparse{<expression>} to calculate the number of rows as \pgfmathparse{ceil(#questions/#columns)} and stored this in \yfin for iteration along the rows. A counter xy is used to hold the Question number \newcounter{xy} initialized to zero \setcounter{xy}{0} and updated each iteration by \addtocounter{xy}{1}. The xcolor package is used for shading. A conditional on xy is used for shading color as fill={\ifnum\the\value{xy}>#1noQcolor\else white\fi} if there is no question. For referencing the table, \label{Tab:#1#2} is used as a unique label for each table.

The code can be much improved, but this at least addresses the OP requirements.

Related Question