Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have the following table that I need to recreate quite a few times

enter image description here

Due to its size and complexity (yes, I am a tex novice) I am looking into ways of automating the creation of said table. I try to think of ways to automating it, but my tex-fu is too weak to do anything with it. What I ultimately want to do is the following

  • Having a command or environment to typeset the table
  • Being able to sum up the points, in three places (24,36 and 60) and store its value
  • Add an extra line if the number of problems exceeds a given threshold (say 10-12) Using the exam package is not an option, as per said time it can not display the points from subquestions. Now I have all the labels for the problems stored (oppgave = problem, Poeng = marks/points) and can refer to them as \label(1.1.a), \label(1.1.b) and so forth. So if possible using these values would be great, however I do not know how to make this into an loop.

I could also type in both the points and the numbers for each problem, but then I ran into difficulties dealing with exparse and understanding the \SplitList command was rather complicated. Especially when it is unknown how many problems /rows I will need. Though it is safe to assume that the number of parts and subparts will never exceed 36 for either Part I nor II.

I also looked into using lat3x syntax with \ExplSyntaxOnobtain something along the lines of

\TypeSettable{ Del1 = 1a1,1a2,1a3, ... , PDel1 = 1,1,1,1, ... , Del2 = 2a, 2b ... }

Here is the code I used to create the table in image

\documentclass[12pt,a4paper]{article}
\usepackage{xparse}
\usepackage{xstring}
\usepackage{booktabs}
\usepackage{mathtools}
\usepackage{tabularx}
\usepackage{multirow}
\usepackage{caption}
\begin{document}
\begin{table}
\caption*{\bf Gjeldende poengfordeling}
{ \noindent \begin{tabular*}{\textwidth}{@{}cl*{18}{@{\;}c@{\;}}}
  \toprule
  & & &  & & &  & & &  & & &  & & & & Sum  \\
  \cmidrule{1-1} \cmidrule(l){2-15} \cmidrule(l){16-17}
  \multirow{2}{*}{Del $1$} & & Oppgave &  $1$a  &  $1$b  &  $1$c
                                       &  $2$a  &  $2$b  &  $2$c 
                                       &  $3$a  &  $3$b  
                                       &  $4$a  &  $4$b  
                                       &  &       \\
                           & & Poeng   &  $1$   &  $1$   &  $1$       
                                       &  $1$   &  $2$   &  $2$  
                                       &  $2$   &  $2$     
                                       &  $2$   &  $2$   &        &      &  & $24$  \\
                             \cmidrule(l){1-17}
  \multirow{2}{*}{}        & &         &  $5$a  &  $5$b  
                                       &  $6$a  &  $6$b   
                                       &  $7$a  &  $7$b  &  &   &    &   &  &         \\
                           & &         &  $2$   &  $2$    
                                       &  $2$   &  $2$ 
                                       &  $2$   &  $2$   &  &   &    &   &  &        \\
  \cmidrule{1-1}\cmidrule(l){2-17}
  \multirow{2}{*}{Del $2$} & & Oppgave &  $1$a  &  $1$b  &  $1$c
                                       &  $2$a  &  $2$b  &  $2$c 
                                       &  $3$a  &  $3$b  &  $3$c \\
%                                      
                           & & Poeng   &  $2$   &  $2$   &  $2$
                                       &  $1$   &  $2$   &  $2$
                                       &  $2$   &  $3$   &  $2$ &   &    &  & &  $36$       \\
%                                       
  \cmidrule(l){1-17}
  \multirow{2}{*}{}        & &         & $4$a   &  $4$b  & $4$c   
                                       & $5$a   &  $5$b   
                                       & $6$a   &  $6$b  & $6$c &       &       &       &      &  &     \\
                           & &         & $3 $   &  $2$   & $3 $  
                                       & $2 $   &  $2$   
                                       & $2 $   &  $2$   & $2$ &       &       &       &      & &   &     \\
  \cmidrule(l){1-15}\cmidrule(l){16-17}
                                                                & \multicolumn{14}{r}{Total antall poeng} &  & $60$  \\
  \bottomrule
  \end{tabular*}}
\end{table}
\end{document}

I did look at a few similar questions to automating table creation, but nothing really sprung to mind =(

In short I do not know what the best path to automating the given table is, but any help and tips and ideas is very much welcome!

share|improve this question
    
Not really a simple task. –  egreg Mar 29 '13 at 13:57
    
Can you also have "Del 3" and so on? –  egreg Mar 29 '13 at 14:12
    
There will only be 2 parts ever. –  N3buchadnezzar Mar 29 '13 at 15:24
    
@N3buchadnezzar oh well the code below would have any number:-) –  David Carlisle Mar 29 '13 at 15:36
1  
have a look at pgfplotstable too- it might be handy here :) –  cmhughes Mar 31 '13 at 18:08

2 Answers 2

up vote 8 down vote accepted

This creates the same table as yours. The \TypesetTable command has also an optional argument for setting the threshold on the number of columns, see the example.

\documentclass{article}
\usepackage[a4paper,margin=1cm]{geometry}
\usepackage{xparse,booktabs}

\ExplSyntaxOn
\NewDocumentCommand{\TypesetTable}{ O{10} m }
 {
  \nebu_typeset_table:nn { #1 } { #2 }
 }

\keys_define:nn { nebutable }
 {
  Del1 .tl_set:N = \l__nebu_deli_tl,
  PDel1 .tl_set:N = \l__nebu_pdeli_tl,
  Del2 .tl_set:N = \l__nebu_delii_tl,
  PDel2 .tl_set:N = \l__nebu_pdelii_tl,
 }

\int_new:N \l__nebu_columns_int
\int_new:N \l__nebu_pdeli_total_int
\int_new:N \l__nebu_pdelii_total_int
\int_new:N \l_nebu_countrows_int
\seq_new:N \l__nebu_deli_seq
\seq_new:N \l__nebu_pdeli_seq
\seq_new:N \l__nebu_delii_seq
\seq_new:N \l__nebu_pdelii_seq
\tl_new:N \l__nebu_preamble_tl
\tl_new:N \l__nebu_table_body_tl

\cs_new_protected:Npn \nebu_typeset_table:nn #1 #2
 {
  \keys_set:nn { nebutable } { #2 }
  \seq_set_split:NnV \l__nebu_deli_seq { , } \l__nebu_deli_tl
  \seq_set_split:NnV \l__nebu_pdeli_seq { , } \l__nebu_pdeli_tl
  \seq_set_split:NnV \l__nebu_delii_seq { , } \l__nebu_delii_tl
  \seq_set_split:NnV \l__nebu_pdelii_seq { , } \l__nebu_pdelii_tl
  % Compute the number of inner columns
  \int_set:Nn \l__nebu_columns_int
   {
    \int_min:nn { #1 }
     {
      \int_max:nn { \seq_count:N \l__nebu_deli_seq }
                  { \seq_count:N \l__nebu_delii_seq }
     }
   }
  % Define the table preamble
  \tl_set:Nx \l__nebu_preamble_tl
   { l c *{ \int_to_arabic:n { \l__nebu_columns_int } } { c } c }
  % Compute the total for Del1
  \int_zero:N \l__nebu_pdeli_total_int
  \seq_map_inline:Nn \l__nebu_pdeli_seq
   { \int_set:Nn \l__nebu_pdeli_total_int { \l__nebu_pdeli_total_int + ##1 } }
  % Compute the total for Del2
  \int_zero:N \l__nebu_pdelii_total_int
  \seq_map_inline:Nn \l__nebu_pdelii_seq
   { \int_set:Nn \l__nebu_pdelii_total_int { \l__nebu_pdelii_total_int + ##1 } }
  % Start building the table contents
  \tl_clear:N \l__nebu_table_body_tl
  \tl_put_right:Nx \l__nebu_table_body_tl
   { \exp_not:N \multicolumn { \int_to_arabic:n { \l__nebu_columns_int + 2 } } { c } { } }
  \tl_put_right:Nn \l__nebu_table_body_tl { & Sum \\ \midrule }

  % Insert Del1
  \__nebu_insert_table_part:nn { deli } { Del~1 }
  \tl_put_right:Nn \l__nebu_table_body_tl { \midrule }
  % Insert Del2
  \__nebu_insert_table_part:nn { delii } { Del~2 }

  % Add total
  \tl_put_right:Nx \l__nebu_table_body_tl
   {
    \exp_not:N \midrule
    \exp_not:N \multicolumn
                { \int_to_arabic:n { 2 + \l__nebu_columns_int } }
                { r }
                { Total~antall~poeng }
    &
    \int_to_arabic:n { \l__nebu_pdeli_total_int + \l__nebu_pdelii_total_int }
    \exp_not:N \\
   }

  % Print the table
  \use:x{ \exp_not:N \begin{tabular}{\l__nebu_preamble_tl} }
  \toprule
  \l__nebu_table_body_tl
  \bottomrule
  \end{tabular}
}

\cs_new_protected:Npn \__nebu_insert_table_part:nn #1 #2
 {
  \int_set:Nn \l_nebu_countrows_int
   {
    \seq_count:c { l__nebu_#1_seq } / \l__nebu_columns_int
   }
  \int_step_inline:nnnn { 1 } { 1 } { \l_nebu_countrows_int }
   {
    % First line
    \int_compare:nTF { ##1 = 1 }
     {
      \tl_put_right:Nn \l__nebu_table_body_tl { #2 & Oppgave & }
     }
     {
      \tl_put_right:Nx \l__nebu_table_body_tl
       { \exp_not:N \cmidrule{3-\int_to_arabic:n { \l__nebu_columns_int + 3 }} }
      \tl_put_right:Nn \l__nebu_table_body_tl { & & }
     }
    \int_step_inline:nnnn 
     { (##1 - 1)*\l__nebu_columns_int + 1 } 
     { 1 }
     { ##1 * \l__nebu_columns_int }
     {
      \tl_put_right:Nx \l__nebu_table_body_tl 
       { \seq_item:cn { l__nebu_#1_seq } { ####1 } & }
     }
     \tl_put_right:Nn \l__nebu_table_body_tl { \\ }
     % Second line
    \int_compare:nTF { ##1 = 1 }
     { \tl_put_right:Nn \l__nebu_table_body_tl { & Poeng & } }
     { \tl_put_right:Nn \l__nebu_table_body_tl { & & } }
     \int_step_inline:nnnn 
      { (##1 - 1)*\l__nebu_columns_int + 1 }
      { 1 }
      { ##1 * \l__nebu_columns_int }
      {
       \tl_put_right:Nx \l__nebu_table_body_tl
        { \seq_item:cn { l__nebu_p#1_seq } { ####1 } & }
      }
      \tl_put_right:Nx \l__nebu_table_body_tl
       {
        \int_compare:nT { ##1 = 1 }
         { \int_to_arabic:n { \use:c { l__nebu_p#1_total_int } } }
        \exp_not:N \\
       }
   }
 }

\ExplSyntaxOff

\setlength{\parindent}{0pt} % just for the example

\begin{document}

\TypesetTable{
  Del1= {1a1,1a2,1a3,1b1,1b2,1b3,1c,1d1,1d2,1d3,1e1,1e2,1f,1g1,1g2,1g3},
  PDel1={1,  1,  1,  1,  2,  2,  2, 2,  0,  2,  2,  2,  2, 2,  2,  0},
  Del2= {2a,2b,2c,2d,2e,2f,3a1,3a2,3b,4a,4b,4c,4d,5a,5b,5c,6a,6b},
  PDel2={2, 2, 2, 2, 2, 2, 2,  2,  3, 2, 2, 2, 2, 1, 2, 2, 2, 2}
}

\bigskip

\TypesetTable[8]{
  Del1= {1a1,1a2,1a3,1b1,1b2,1b3,1c,1d1,1d2,1d3,1e1,1e2,1f,1g1,1g2,1g3},
  PDel1={1,  1,  1,  1,  2,  2,  2, 2,  0,  2,  2,  2,  2, 2,  2,  0},
  Del2= {2a,2b,2c,2d,2e,2f,3a1,3a2,3b,4a,4b,4c,4d,5a,5b,5c,6a,6b},
  PDel2={2, 2, 2, 2, 2, 2, 2,  2,  3, 2, 2, 2, 2, 1, 2, 2, 2, 2}
}


\end{document}

enter image description here


This is a new implementation that allows to gather the exercises label and grades during the document and print the table anywhere; it requires two runs before stabilizing, as it uses the .aux file.

The \addtoDel macro receives three arguments; the first is 1 or 2, for the two kinds, the second is the label and the third is the grade. You'll probably be using it in the macro for typesetting the exercises.

The new \TypesetTable command has only one optional argument to set the threshold number of inner columns.

\documentclass{article}
\usepackage[a4paper,margin=1cm]{geometry}
\usepackage{xparse,booktabs}

\ExplSyntaxOn

\NewDocumentCommand{\TypesetTable} { O{10} }
 {
  \seq_if_empty:NT \g__nebu_deli_seq
   {
    \seq_gput_right:Nn \g__nebu_deli_seq { X }
    \seq_gput_right:Nn \g__nebu_pdeli_seq { 0 }
    \seq_gput_right:Nn \g__nebu_delii_seq { X }
    \seq_gput_right:Nn \g__nebu_pdelii_seq { 0 }
   }
  \nebu_typeset_table:n { #1 }
 }

\NewDocumentCommand{\addtoDel}{mmm}
 {
  \clist_gput_right:cn { g__nebu_del\int_to_roman:n { #1 }_clist } { #2 }
  \clist_gput_right:cn { g__nebu_pdel\int_to_roman:n { #1 }_clist } { #3 }
 }

\AtEndDocument{
  \iow_shipout:cx { @auxout }
   { \exp_not:N \LoadDel { i } { \g__nebu_deli_clist } }
  \iow_shipout:cx { @auxout }
   { \exp_not:N \LoadPDel { i } { \g__nebu_pdeli_clist } }
  \iow_shipout:cx { @auxout }
   { \exp_not:N \LoadDel { ii } { \g__nebu_delii_clist } }
  \iow_shipout:cx { @auxout }
   { \exp_not:N \LoadPDel { ii } { \g__nebu_pdelii_clist } }
}
\cs_generate_variant:Nn \iow_shipout:Nx { c }
\NewDocumentCommand{\LoadDel}{mm}
 {
  \seq_gset_split:cnn { g__nebu_del#1_seq } { , } { #2 }
 }
\NewDocumentCommand{\LoadPDel}{mm}
 {
  \seq_gset_split:cnn { g__nebu_pdel#1_seq } { , } { #2 }
 }
\cs_generate_variant:Nn \seq_gset_split:Nnn { c }

\int_new:N \l__nebu_columns_int
\int_new:N \l__nebu_pdeli_total_int
\int_new:N \l__nebu_pdelii_total_int
\int_new:N \l_nebu_countrows_int
\seq_new:N \g__nebu_deli_seq
\seq_new:N \g__nebu_pdeli_seq
\seq_new:N \g__nebu_delii_seq
\seq_new:N \g__nebu_pdelii_seq
\clist_new:N \g__nebu_deli_clist
\clist_new:N \g__nebu_pdeli_clist
\clist_new:N \g__nebu_delii_clist
\clist_new:N \g__nebu_pdelii_clist
\tl_new:N \l__nebu_preamble_tl
\tl_new:N \l__nebu_table_body_tl

\cs_new_protected:Npn \nebu_typeset_table:n #1
 {
  % Compute the number of inner columns
  \int_set:Nn \l__nebu_columns_int
   {
    \int_min:nn { #1 }
     {
      \int_max:nn { \seq_count:N \g__nebu_deli_seq }
                  { \seq_count:N \g__nebu_delii_seq }
     }
   }
  % Define the table preamble
  \tl_set:Nx \l__nebu_preamble_tl
   { l c *{ \int_to_arabic:n { \l__nebu_columns_int } } { c } c }
  % Compute the total for Del1
  \int_zero:N \l__nebu_pdeli_total_int
  \seq_map_inline:Nn \g__nebu_pdeli_seq
   { \int_set:Nn \l__nebu_pdeli_total_int { \l__nebu_pdeli_total_int + ##1 } }
  % Compute the total for Del2
  \int_zero:N \l__nebu_pdelii_total_int
  \seq_map_inline:Nn \g__nebu_pdelii_seq
   { \int_set:Nn \l__nebu_pdelii_total_int { \l__nebu_pdelii_total_int + ##1 } }
  % Start building the table contents
  \tl_clear:N \l__nebu_table_body_tl
  \tl_put_right:Nx \l__nebu_table_body_tl
   { \exp_not:N \multicolumn { \int_to_arabic:n { \l__nebu_columns_int + 2 } } { c } { } }
  \tl_put_right:Nn \l__nebu_table_body_tl { & Sum \\ \midrule }

  % Insert Del1
  \__nebu_insert_table_part:nn { deli } { Del~1 }
  \tl_put_right:Nn \l__nebu_table_body_tl { \midrule }
  % Insert Del2
  \__nebu_insert_table_part:nn { delii } { Del~2 }

  % Add total
  \tl_put_right:Nx \l__nebu_table_body_tl
   {
    \exp_not:N \midrule
    \exp_not:N \multicolumn
                { \int_to_arabic:n { 2 + \l__nebu_columns_int } }
                { r }
                { Total~antall~poeng }
    &
    \int_to_arabic:n { \l__nebu_pdeli_total_int + \l__nebu_pdelii_total_int }
    \exp_not:N \\
   }

  % Print the table
  \use:x{ \exp_not:N \begin{tabular}{\l__nebu_preamble_tl} }
  \toprule
  \l__nebu_table_body_tl
  \bottomrule
  \end{tabular}
}

\cs_new_protected:Npn \__nebu_insert_table_part:nn #1 #2
 {
  \int_set:Nn \l_nebu_countrows_int
   {
    \seq_count:c { g__nebu_#1_seq } / \l__nebu_columns_int
   }
  \int_step_inline:nnnn { 1 } { 1 } { \l_nebu_countrows_int }
   {
    % First line
    \int_compare:nTF { ##1 = 1 }
     {
      \tl_put_right:Nn \l__nebu_table_body_tl { #2 & Oppgave & }
     }
     {
      \tl_put_right:Nx \l__nebu_table_body_tl
       { \exp_not:N \cmidrule(lr){3-\int_to_arabic:n { \l__nebu_columns_int + 2 }} }
      \int_compare:nT { ##1 = 2 }
       {\tl_put_right:Nx \l__nebu_table_body_tl
         { \exp_not:N \cmidrule(l{3em}r)
            {\int_to_arabic:n { \l__nebu_columns_int + 2 }-
             \int_to_arabic:n { \l__nebu_columns_int + 3 }}
         }
       }
      \tl_put_right:Nn \l__nebu_table_body_tl { & & }
     }
    \int_step_inline:nnnn 
     { (##1 - 1)*\l__nebu_columns_int + 1 } 
     { 1 }
     { ##1 * \l__nebu_columns_int }
     {
      \tl_put_right:Nx \l__nebu_table_body_tl 
       { \seq_item:cn { g__nebu_#1_seq } { ####1 } & }
     }
     \tl_put_right:Nn \l__nebu_table_body_tl { \\ }
     % Second line
    \int_compare:nTF { ##1 = 1 }
     { \tl_put_right:Nn \l__nebu_table_body_tl { & Poeng & } }
     { \tl_put_right:Nn \l__nebu_table_body_tl { & & } }
     \int_step_inline:nnnn 
      { (##1 - 1)*\l__nebu_columns_int + 1 }
      { 1 }
      { ##1 * \l__nebu_columns_int }
      {
       \tl_put_right:Nx \l__nebu_table_body_tl
        { \seq_item:cn { g__nebu_p#1_seq } { ####1 } & }
      }
      \tl_put_right:Nx \l__nebu_table_body_tl
       {
        \int_compare:nT { ##1 = 1 }
         { \int_to_arabic:n { \use:c { l__nebu_p#1_total_int } } }
        \exp_not:N \\
       }
   }
 }

\ExplSyntaxOff

\setlength{\parindent}{0pt} % just for the example

\begin{document}

\TypesetTable

\bigskip

\TypesetTable[6]

\addtoDel{1}{1a1}{2}
\addtoDel{1}{1a2}{1}
\addtoDel{1}{1a3}{1}
\addtoDel{1}{1b}{2}
\addtoDel{1}{1c}{2}

\addtoDel{2}{a1}{2}
\addtoDel{2}{a2}{1}
\addtoDel{2}{a3}{1}
\addtoDel{2}{b}{2}
\addtoDel{2}{c}{2}
\addtoDel{2}{d1}{2}
\addtoDel{2}{d2}{2}
\addtoDel{2}{e}{2}
\addtoDel{2}{f1}{2}
\addtoDel{2}{f2}{2}
\addtoDel{2}{f4}{2}
\addtoDel{2}{f5}{2}

\end{document}

enter image description here

share|improve this answer
    
It was a tough decision, but after a few tweaks your solution came out ahead! Very nice solutions to both of you. –  N3buchadnezzar Mar 30 '13 at 16:03

I got a bit bored with the formatting but

enter image description here

from

\maketab{
{1a1,1; 1a2,1;1a3,1;1b1,1;1b3,2;1c,2;1d1,2;1d2,2;1d3,0;1e1,2;1e2,2;
     1e3,2;1f,2;1g1,2;1g2,2;1g3,2;}
{2a,2;2b,2;2c,2;2d,2;2e,2;2f,2;3a1,2;3a2,2;3b,3;4a,2;4b,2;
     4c,2;4d,2;5a,1;5b,2;5c,2;6a,2;6b,2;}}

Full document:

\documentclass{article}
\usepackage{array}

\newcount\delcnt
\newcount\thistot
\newcount\tottot
\extrarowheight3pt
\makeatletter

\def\maketab#1{%
\delcnt\z@
\global\tottot\z@
\begin{tabular}{|c|p{20em}|c|}
\@maketab#1\relax
\multicolumn{2}{r|}{Total}&\the\tottot
\end{tabular}}

\def\@maketab#1{%
\ifx\relax#1%
\expandafter\@gobble
\else
\global\thistot\z@
\expandafter\@firstofone
\fi
{\global\advance\delcnt\@ne
Del \the\delcnt&%
\@maketb@#1\relax,;%
&
\global\advance\tottot\thistot
\the\thistot\\\hline\@maketab}}


\def\@maketb@#1,#2;{%
\ifx\relax#1\else
\global\advance\thistot#2\relax
\parbox[t]{2em}{\centering
\strut\ignorespaces#1\par\vskip2pt\hrule\vskip2pt
\strut\ignorespaces#2}\hfil\penalty\z@\hfilneg
\expandafter\@maketb@
\fi}

\makeatother

\begin{document}

\maketab{
{1a1,1; 1a2,1;1a3,1;1b1,1;1b3,2;1c,2;1d1,2;1d2,2;1d3,0;1e1,2;1e2,2;1e3,2;1f,2;1g1,2;1g2,2;1g3,2;}
{2a,2;2b,2;2c,2;2d,2;2e,2;2f,2;3a1,2;3a2,2;3b,3;4a,2;4b,2;4c,2;4d,2;5a,1;5b,2;5c,2;6a,2;6b,2;}}


\end{document}
share|improve this answer
    
I think I prefer this answer, as I actually understand enough of it to make aesthetics changes =) pastebin.com/esFZYTdQ Is there any way to obtain a underline beneath the point counters? ^^ –  N3buchadnezzar Mar 30 '13 at 0:35
    
The individual entries are set in a parbox \parbox[t]{2em}{\centering \strut\ignorespaces#1\par\vskip2pt\hrule\vskip2pt \strut\ignorespaces#2} I think you just want another \par\vskip2pt\hrule\vskip2pt at the end of that box after #2 –  David Carlisle Mar 30 '13 at 0:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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