Dynamic number interval conversion table

for looptables

I am a teacher and I need to use for all of my exams a table converting intervals of so called "value points" to points comprised between 0 and 20. I use the following table:

\documentclass[addpoints,12pt]{exam}


\newcolumntype{?}[1]{!{\vrule width #1}} % to have bold vertical line in tabular
\usepackage{makecell} % to be able to use \\  linebreaks in tabular

\begin{document} 

\setlength\tabcolsep{0.25em}
{\renewcommand{\arraystretch}{1}% for the vertical padding
\begin{tabular}{| p{1.3cm} |c | c | c | c | c |  c | c |  c | c |  c  ?{1mm} c |  c |  c | c |  c |  c | c | c | c | c | c  |}
\hline
Value points & \makecell{0\\-\\3}  & \makecell{4\\-\\7}  & \makecell{8\\-\\11}  & \makecell{12\\-\\15}  & \makecell{16\\-\\19}  & \makecell{20\\-\\23}  & \makecell{24\\-\\27}  & \makecell{28\\-\\31} & \makecell{32\\-\\35}  & \makecell{36\\-\\39} & 
\makecell{40\\-\\43}  & \makecell{44\\-\\47}  & \makecell{48\\-\\51}  & \makecell{52\\-\\55}  & \makecell{56\\-\\59}  & \makecell{60\\-\\63}  & \makecell{64\\-\\67}  & \makecell{68\\-\\71}  & \makecell{72\\-\\75}  & \makecell{76\\-\\79}  & \makecell{80\\-\\83}  \\
\hline
Marks & 0&  1 & 2 & 3 & 4 & 5 & 6& 7& 8 & 9 & 10 & 11 & 12 & 13 & 14 &15 & 16 & 17 & 18 & 19 & 20  \\
\hline
\end{tabular}
}

\end{document} 

These value points are different for all exams (in this example the maximum number is 83, but it may be any other number), whereas points are always between 0 and 20. So the the second line in the table should be fixed, and the number of columns also. I would like to fill the first line dynamically depending on the maximum number, so somehow every cell in the first row should be filled in a for loop. How could I achieve this?

Best Answer

With the table dimensions being fixed, you can set a number of formulae in each cell that define the range. Below I define \lrange{<num>} and \urange{<num>} where you specify the lower and upper range block (from 0 to 20) and xfp is used to figure out the corresponding lower/upper range values. The total/maximum score for the test is held inside \ms (but this could be changed to automatically pick up the total score on the test).

enter image description here

\documentclass[addpoints,12pt]{exam}

\usepackage{booktabs,xfp,array}

\newcommand{\ms}{83}
\newcommand{\passfail}{10}
\newcommand{\lrange}[1]{%
  \ifnum#1<\passfail
    %\itshape% fail
  \else
    \bfseries% pass
  \fi
  \fpeval{#1 > 0 ? floor((\ms / 21) * #1 + 1, 0) : 0}
}%
\newcommand{\urange}[1]{%
  \ifnum#1<\passfail
    %\itshape% fail
  \else
    \bfseries% pass
  \fi
  \fpeval{floor((\ms / 21) * (#1 + 1), 0)}%
}

\begin{document} 

\begingroup
  \small
  \setlength\tabcolsep{0.25em}
  \renewcommand{\arraystretch}{1}% for the vertical padding
  \begin{tabular}{ r *{21}{ w{c}{1em} } }
    \toprule
      & \multicolumn{21}{c}{Point range on this test (\textbf{pass} / fail)} \\
    \cmidrule{2-22}
    from & \lrange{0} & \lrange{1} & \lrange{2} & \lrange{3} & \lrange{4} & \lrange{5} & \lrange{6} & \lrange{7} & \lrange{8} & \lrange{9} & \lrange{10} &
      \lrange{11} & \lrange{12} & \lrange{13} & \lrange{14} & \lrange{15} & \lrange{16} & \lrange{17} & \lrange{18} & \lrange{19} & \lrange{20} \\[\dimexpr-\normalbaselineskip+0.7em]
         & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- & -- \\[\dimexpr-\normalbaselineskip+0.8em]
    to   & \urange{0} & \urange{1} & \urange{2} & \urange{3} & \urange{4} & \urange{5} & \urange{6} & \urange{7} & \urange{8} & \urange{9} & \urange{10} &
      \urange{11} & \urange{12} & \urange{13} & \urange{14} & \urange{15} & \urange{16} & \urange{17} & \urange{18} & \urange{19} & \bfseries\ms \\
    \midrule
    Marks & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20  \\
    \bottomrule
  \end{tabular}
\endgroup

\end{document} 

Of course, I played around with the formatting, which you can revert to your style.

Related Question