1

I want to store table lines with \addtocontents and generate a table afterwards from this entries with \@starttoc. Because \@starttoc uses \begingroup and \endgroup it makes problems with tables (! Extra }, or forgotten \endgroup). So I tried to define my own \starttoc command based on the definition of \@starttoc without the group macros, which now actually compiles:

\documentclass{article}

\makeatletter

\newcommand\addEntry[2]{#1&#2\}

\newcommand\mystarttoc[1]{@input{\jobname.#1}\if@filesw\expandafter\newwrite\csname tf@#1\endcsname\immediate\openout\csname tf@#1\endcsname\jobname.#1\relax\fi}

\begin{document}

\begin{tabular}{|l|l|} \addEntry{\textbf{ID}}{\textbf{Description}} \mystarttoc{damageClassA} % summarize damages with class A %\mystarttoc{damageClassA}\ % would not work cause damageClassA contains multiple table lines \mystarttoc{damageClassB} % summarize damages with class B \mystarttoc{damageClassC} % should not extend table cause there are no class C damages in this MWE \end{tabular}

\addtocontents{damageClassA}{\addEntry{A-1}{damage1 with classification A}} \addtocontents{damageClassA}{\addEntry{A-2}{damage2 with classification A}} \addtocontents{damageClassA}{\addEntry{A-X}{damage...X with classification A}} \addtocontents{damageClassB}{\addEntry{B-1}{damage1 with classification B}}

\makeatother

\end{document}

However, it seems that the if-clause of the starttoc macro introduces some superfluous spaces which results in a misaligned foo4 line and a superfluous empty table line:

enter image description here

Can this be prevented somehow or should I use a completely different approach? LaTeX2e syntax would be prefered over expl syntax.

UPDATE This is actually a follow up question from How to automatically generate data summary at the beginning of the document? I updated my MWE so that hopefully my intention is more clear.

I want to summarize 0..X damages with classification A in a file damageClassA. And the same for classes B and C. So there will be only 3 files at a time. But each file should be able to handle multiple table lines.

And the final table will actually be a bit more complex. So using something other than a table to get the current visual result will unfortunately not be a solution either.

Simon Dispa
  • 39,141

3 Answers3

1

UPDATE

Since you are willing to try a new approach, I will offer a database based one using the datatool package.

First the database is created and loaded with the data from a .cvs file.

The main command \DTLforeach allows you to traverse the database, retrieving each row and performing tasks. For example, use some columns to complete a table. (section 2)

More interesting, the rows can be selected based on a criteria. (section 3)

In section 4, the custom command \Desc {ID} will retrieve the corresponding description.

b

\documentclass[a4paper,12pt]{article}

\usepackage{datatool}

% raw data \begin{filecontents}[overwrite]{damagesclass.csv} "Type", "ID", "Description" A, A-1, damage1 with classification A A, A-2, damage2 with classification A A, A-X, damage ... X with classification A B, B-1, damage1 with classification B \end{filecontents}

% Creates a new database named damages
% and fills it with the data
% from the CSV (comma-separated value) damagesclass.csv
\DTLloaddb{damages}{damagesclass.csv}

\begin{document}

\section{Show all the data base content}

\DTLdisplaydb{damages} % show all the content

\section{Table of selected columns (#2 and #3)}

\renewcommand{\arraystretch}{1.8} \noindent\begin{tabular}{|c| l |} \hline {\bfseries ID} & {\bfseries Description} \DTLforeach{damages}{\Type=Type,\ID=ID, \Description=Description} { \ \ID &\Description }% display selected file content \ \hline
\end{tabular}

\section{Table of selected columns and selected rows (Type =A)}

\noindent\begin{tabular}{|c| l |} \hline {\bfseries ID} & {\bfseries Description} \DTLforeach[\DTLiseq{\Type}{A}]{damages}{\Type=Type,\ID=ID, \Description=Description} {\DTLiffirstrow{\ \hline}{\} \ID &\Description }% display selected file content \ \hline
\end{tabular}

\section{Find a single cell}

To qualify it required to have a \emph{\DTLfetch{damages}{ID}{A-2}{Description}}.

\end{document}

Simon Dispa
  • 39,141
  • Actually there could be multiple table lines in one toc file - see my extended example. \mystarttoc{test1}\\ would result in foo2 & barfoo3 & bar \\. So the table line ending \\ have to be handled by \addEntry. – Cryptkeeper Oct 04 '21 at 16:17
  • @Cryptkeeper Please see the updated answer with multiple table lines. – Simon Dispa Oct 04 '21 at 16:41
  • You are opening 4 files for writing, for every line one. – Ulrike Fischer Oct 04 '21 at 16:42
  • @Ulrike Fischer Sure. It is wrong in the MWE. I am not sure what the OP wants to achieve. – Simon Dispa Oct 04 '21 at 17:32
  • @SimonDispa Thanks for your willingness and sorry if my intention was not really clear. Please see my updated question: There will ever only be 3 files at a time. And each file could contain 0...X table lines. So manually placing the \\ correctly in the tabular environment will not work. – Cryptkeeper Oct 04 '21 at 21:20
1

You can suppress the unwanted space with \ignorespaces. To avoid the empty line the first entry has to be different from the followings. The following assumes that you have only one such tabular.

\documentclass{article}

\makeatletter

\newcommand\addEntry[2]{#1&#2\global\let\addEntry\addEntrycont} \newcommand\addEntrycont[2]{\#1&#2}

\newcommand\mystarttoc[1]{% @input{\jobname.#1}% \if@filesw\expandafter\newwrite\csname tf@#1\endcsname\immediate\openout\csname tf@#1\endcsname\jobname.#1\relax\fi \ignorespaces}

\makeatother \begin{document}

\begin{tabular}{|l|l|} \addEntry{\textbf{ID}}{\textbf{Description}} \mystarttoc{damageClassA} % summarize damages with class A %\mystarttoc{damageClassA}\ % would not work cause damageClassA contains multiple table lines \mystarttoc{damageClassB} % summarize damages with class B \mystarttoc{damageClassC} % should not extend table cause there are no class C damages in this MWE \end{tabular}

\addtocontents{damageClassA}{\addEntry{A-1}{damage1 with classification A}} \addtocontents{damageClassA}{\addEntry{A-2}{damage2 with classification A}} \addtocontents{damageClassA}{\addEntry{A-X}{damage...X with classification A}} \addtocontents{damageClassB}{\addEntry{B-1}{damage1 with classification B}}

\makeatother

\end{document}

If you know that you will always have a title line you could also do something like this:

\documentclass{article}

\makeatletter

\newcommand\addTitleEntry[2]{#1&#2} \newcommand\addEntry[2]{\#1&#2}

\newcommand\mystarttoc[1]{% @input{\jobname.#1}% \if@filesw\expandafter\newwrite\csname tf@#1\endcsname\immediate\openout\csname tf@#1\endcsname\jobname.#1\relax\fi \ignorespaces}

\makeatother \begin{document}

\begin{tabular}{|l|l|} \addTitleEntry{\textbf{ID}}{\textbf{Description}} \mystarttoc{damageClassA} % summarize damages with class A %\mystarttoc{damageClassA}\ % would not work cause damageClassA contains multiple table lines \mystarttoc{damageClassB} % summarize damages with class B \mystarttoc{damageClassC} % should not extend table cause there are no class C damages in this MWE \end{tabular}

\addtocontents{damageClassA}{\addEntry{A-1}{damage1 with classification A}} \addtocontents{damageClassA}{\addEntry{A-2}{damage2 with classification A}} \addtocontents{damageClassA}{\addEntry{A-X}{damage...X with classification A}} \addtocontents{damageClassB}{\addEntry{B-1}{damage1 with classification B}}

\makeatother

\end{document}

Ulrike Fischer
  • 327,261
1

I propose a slightly different approach. With \printsummary you define the classes you want, producing the table with the data collected in the previous run. Next the “toc” files are opened.

The table body is populated before typesetting using \CatchFileDef, so we can control whether a file is empty.

\documentclass{article}
\usepackage{catchfile}

\makeatletter \newcommand\addEntry[2]{#1&#2\}

\newcommand{\printsummary}[1]{% \def\class@summary{}% @for\next:=#1\do{% \CatchFileDef\temp{\jobname.\next}{}% \ifx\temp@empty \else \edef\class@summary{% \unexpanded\expandafter{\class@summary}% \unexpanded\expandafter{\temp}% }% \fi }% \begin{tabular}{|l|l|} \hline \addEntry{\textbf{ID}}{\textbf{Description}} \hline \class@summary \hline \end{tabular} % now open the files \if@filesw @for\next:=#1\do{% \expandafter\newwrite\csname tf@\next\endcsname \immediate\openout\csname tf@\next\endcsname\jobname.\next\relax }% \fi } \makeatother

\begin{document}

\printsummary{damageClassA,damageClassB,damageClassC}

\addtocontents{damageClassA}{\addEntry{A-1}{damage1 with classification A}} \addtocontents{damageClassA}{\addEntry{A-2}{damage2 with classification A}} \addtocontents{damageClassA}{\addEntry{A-X}{damage...X with classification A}} \addtocontents{damageClassB}{\addEntry{B-1}{damage1 with classification B}}

\end{document}

enter image description here

A different approach that doesn't require any auxiliary file and allows to avoid defining classes beforehand. It just assumes that the first argument to \addentry is an uppercase letter.

The command \addentry writes a suitable line in the .aux file. When the .aux file is read in at begin document, the entries will populate a sequence. The command \printsummary will sort the sequence based on the first argument (but not modifying the order of entries within the same class) and print the table.

\documentclass{article}

\ExplSyntaxOn \NewDocumentCommand{\printsummary}{} { \cryptkeeper_summary_print: }

\NewDocumentCommand{\addentry}{mmm} { \iow_now:cn { @auxout } { \summaryentry{#1}{#2}{#3} } }

\NewDocumentCommand{\summaryentry}{mmm} { \seq_gput_right:Nn \g_cryptkeeper_summary_items_seq { {#1}{#2}{#3} } }

\seq_new:N \g_cryptkeeper_summary_items_seq

\cs_new_protected:Nn \cryptkeeper_summary_print: { % sort the sequence based on the first item part \seq_gsort:Nn \g_cryptkeeper_summary_items_seq { \int_compare:nTF { __cryptkeeper_summary_sort:nnn ##1 > __cryptkeeper_summary_sort:nnn ##2 } { \sort_return_swapped: } { \sort_return_same: } } \begin{tabular}{|l|l|} \hline \textbf{ID} & \textbf{Description} \ \hline \seq_map_function:NN \g_cryptkeeper_summary_items_seq \cryptkeeper_summary_print_entry:n \hline \end{tabular} }

\cs_new:Nn __cryptkeeper_summary_sort:nnn { \int_from_alph:n { #1 } }

\cs_new_protected:Nn \cryptkeeper_summary_print_entry:n { __cryptkeeper_summary_print_entry:nnn #1 } \cs_new_protected:Nn __cryptkeeper_summary_print_entry:nnn { #1-#2 & #3 \ }

\ExplSyntaxOff

\begin{document}

\printsummary

\addentry{A}{1}{damage1 with classification A} \addentry{A}{2}{damage2 with classification A} \addentry{B}{1}{damage1 with classification B} \addentry{A}{X}{damage...X with classification A}

\end{document}

The output is the same as before.

egreg
  • 1,121,712