# Automatic generation of table that shows groupings

In my document, I'm using one command to create numbered elements and another command to group all elements up to the next grouping command. I'm trying to create a table that shows these groupings, as in the following example:

\documentclass{article}

\usepackage{tabularx}
\newcolumntype{x}{>{\centering\arraybackslash\hspace{0pt}}p{0.9cm}}
\newcolumntype{Y}{>{\centering\arraybackslash}X}

\newcounter{Elements}
\setcounter{Elements}{1}

\newcommand{\newgrouping}[1]{#1 starts.\\}

\begin{document}
\newgrouping{Some}
\newelement
\newelement
\newgrouping{Stuff}
\newelement
\newelement
\newelement
\newgrouping{Here}
\newelement

First table:
\begin{tabular}{|@{}c@{}|@{}c@{}|@{}c@{}|}
\hline
\begin{tabularx}{2cm}{Y|Y} 1 & 2 \end{tabularx} & \begin{tabularx}{2cm}{Y|Y|Y} 3 & 4 & 5 \end{tabularx} & \begin{tabularx}{2cm}{Y} 6 \end{tabularx} \\
\hline
Some & Stuff & Here \\ \hline
\end{tabular}

\vspace{3em}
Second table:
\begin{tabular}{|x|x|x|x|x|x|}
\hline
1 & 2 & 3 & 4 & 5 & 6 \\
\hline
\multicolumn{2}{|c|}{Some} & \multicolumn{3}{c|}{Stuff} & \multicolumn{1}{c|}{Here} \\ \hline
\end{tabular}

\end{document}


You can see the output in this image:

In the above code, the tables are hard-coded. I want them to automatically adapt to new groupings and elements. I would prefer the first table version, but since I believe the second version is somewhat simpler, I post my unfinished code for building the second table version.

With the help of some other posts, I got this far (code should compile). Now I'm unable to create the second row of the table automatically:

\documentclass{article}

\usepackage{tabularx}
\newcolumntype{x}{>{\centering\arraybackslash\hspace{0pt}}p{0.9cm}}
\makeatletter
\newcolumntype{\expand}{}
\long\@namedef{NC@rewrite@\string\expand}{\expandafter\NC@find}
\makeatother

\newcounter{Elements}
\setcounter{Elements}{1}

\newcounter{ElementsInGroup}
\setcounter{ElementsInGroup}{0}
\newcounter{GroupNumber}
\setcounter{GroupNumber}{0}
\newcommand{\currentgroupname}{}
\newcommand{\tablealignment}{}
\newcommand{\elementnumberrow}{}
\newcommand{\groupnamesrow}{}
\newcommand{\oldgroupnamesrow}{}
\newcommand{\currentgroupnamesrow}{}

\newcommand{\newgrouping}[1]{#1 starts.\\
\setcounter{ElementsInGroup}{0}
\renewcommand{\currentgroupname}{#1}
\expandafter\edef\expandafter\oldgroupnamesrow\expandafter{\groupnamesrow}
}
\newcommand{\newelement}{Element \theElements.\\
\expandafter\edef\expandafter\elementnumberrow\expandafter{\elementnumberrow \ifnum\theElements=1{}\else&\fi \theElements}
\expandafter\edef\expandafter\currentgroupnamesrow\expandafter{\theElementsInGroup \currentgroupname} % !!
\expandafter\edef\expandafter\groupnamesrow\expandafter{\oldgroupnamesrow \ifnum\theGroupNumber=1{}\else,\fi \currentgroupnamesrow} % need to replace , with &
\expandafter\def\expandafter\tablealignment\expandafter{\tablealignment x|}

\begin{document}
\newgrouping{Some}
\newelement
\newelement
\newgrouping{Stuff}
\newelement
\newelement
\newelement
\newgrouping{Here}
\newelement

\groupnamesrow % prints 2Some,3Stuff,1Here

\begin{tabular}{|\expand\tablealignment}
\hline
\elementnumberrow \\
\hline
\multicolumn{2}{|c|}{Some} & \multicolumn{3}{c|}{Stuff} & \multicolumn{1}{c|}{Here}
\\ \hline
\end{tabular}

\end{document}


The problem is that, as soon as I add \multicolumn to the line with the !!-comment, I receive the error "undefined control sequence \newelement". I had similar problems when I tried to use tabularx for the first version of the table.

After reading some posts, I wonder if it is simply not possible to use multicolumn or tabularx in this context, where \edef is needed? If that is true, maybe there is some other way to achieve what I am looking for?

• Try \xdef instead of \edef -- your \edef'ed command is hidden in the group which is formed by the table cells, so \newelement is invisible outside – user31729 Sep 1 '16 at 18:46
• @ChristianHupfer Changing the critical line to \expandafter\xdef\expandafter\currentgroupnamesrow\expandafter{\multicolumn{\theElementsInGroup}{c}{\currentgroupname} } % !! produces the same error (also when I replace other \edefs with \xdef). But if I remove \multicolumn it works, as with \edef, it just doesn't produce what I need. – nyxl Sep 1 '16 at 18:53
• I have no time to do this, but I recommend the usage of expl3 features for this. It seems to be easier. – user31729 Sep 2 '16 at 16:43
• That is a good hint. I think I can get it to work with expl3 features (I have never used them before). I'm going to answer again when it's (hopefully) working. – nyxl Sep 3 '16 at 12:00

As suggested by Christian, I have used expl3 features to solve my problem. I haven't been able to find a way to generate the second version of the table, but since the first version is my favorite anyway, that is not a problem.

There are certainly more elegant ways of solving this than my solution. In particular, I generate a string that contains alignment and content of the cells I want to create first, which I then parse using expl3 features to generate the row with fitting tabularxs, which is overly complicated. However, in my application, I also have to store the data like this - Using later defined macro values - and I'm not sure how to do that with expl3 sequences.

I have also found this very helpful: How can I combine \seq_use:Nn and \seq_map_inline:Nn? but I couldn't find a way to produce multicolumns using elements from two sequences instead of just one, which is why I wasn't able to produce the second version of my table.

This is my code for the first version:

\documentclass{article}

\usepackage{tabularx}
\usepackage{xparse}

\newcolumntype{Y}{>{\centering\arraybackslash}X}

\makeatletter
\newcolumntype{\expand}{}
\long\@namedef{NC@rewrite@\string\expand}{\expandafter\NC@find}
\makeatother

\newcounter{Elements}
\setcounter{Elements}{1}
\newcounter{ElementsInGroup}
\setcounter{ElementsInGroup}{0}
\newcounter{GroupNumber}
\setcounter{GroupNumber}{0}
\newcommand{\currenttabularxalignment}{}
\newcommand{\currentgroupelements}{}
\newcommand{\elementrowstring}{moo} % nonsense element, to be removed later
\newcommand{\oldelementrowstring}{}
\newcommand{\groupnamesrow}{}

\newcommand{\newgrouping}[1]{#1~starts.\\
\expandafter\edef\expandafter\groupnamesrow\expandafter{\groupnamesrow\ifnum\theGroupNumber=1{}\else&\fi#1}
\expandafter\edef\expandafter\oldelementrowstring\expandafter{\elementrowstring}
\renewcommand{\currentgroupelements}{}
\renewcommand{\currenttabularxalignment}{}
\setcounter{ElementsInGroup}{0}
}
\newcommand{\newelement}{Element~\theElements.\\
\expandafter\edef\expandafter\currentgroupelements\expandafter{\currentgroupelements \ifnum\theElementsInGroup=0{}\else&\fi \theElements}
\expandafter\edef\expandafter\currenttabularxalignment\expandafter{\currenttabularxalignment\ifnum\theElementsInGroup=0{}\else|\fi Y}
\expandafter\edef\expandafter\elementrowstring\expandafter{\oldelementrowstring;\currenttabularxalignment,\currentgroupelements}

\begin{document}
\newgrouping{Some}
\newelement
\newelement
\newgrouping{Stuff}
\newelement
\newelement
\newelement
\newgrouping{Here}
\newelement

% now \elementrowstring looks like
% moo;Y|Y,1&2;Y|Y|Y,3&4&5;Y,6
% and we make a table row from that

% \renewcommand{\elementrowstring}{moo;Y|Y,1&2;Y|Y|Y,3&4&5;Y,6}

\ExplSyntaxOn
\seq_new:N \l_nyxl_groups
\seq_new:N \l_nyxl_groupboth

\cs_generate_variant:Nn \seq_set_split:Nnn {Nnx} % define \seq_set_split:Nnx that expands the last argument
\seq_set_split:Nnx \l_nyxl_groups { ; } {\elementrowstring}

\cs_new:Npn \nyxl_elementgroupcell:n #1
{
& \seq_set_split:Nnn \l_nyxl_groupboth { , } {#1}
\seq_get_left:NN \l_nyxl_groupboth \l_tmpa_tl
\seq_get_right:NN \l_nyxl_groupboth \l_tmpb_tl
\begin{tabularx}{2cm}{\expand\l_tmpa_tl}
\l_tmpb_tl
\end{tabularx}
}
\cs_new:Npn \nyxl_elementsrow:
{
\seq_map_function:NN \l_nyxl_groups \nyxl_elementgroupcell:n
}

\seq_pop_left:NN \l_nyxl_groups \l_tmpa_tl %pop first nonsense-element

\begin{tabular}{|c|*{\theGroupNumber}{@{}c@{}|}}
\hline
\textit{Elements} \nyxl_elementsrow: \\
\hline
\textit{Groups} & \groupnamesrow \\
\hline
\end{tabular}

\ExplSyntaxOff
\end{document}


which produces