3

I have a list of keywords and descriptions for those keywords. I would like to have tables for the different contexts in which these are used, containing only the relevant ones for the case. Some of these keywords repeat on the different contexts, so it would be ideal to write the description only once and then "reference" that description in each of my tables for ease of maintenance.

Example:

\section{Runtype 1 description}
\begin{table}
\begin{center}
\begin{tabular}{ l l }
runtype & Keyword to determine runtype. Can be 1 or 2. \\
        & I don't want to hardcode this description twice...\\
param_1 & Keyword to determine the parameter of runtype = 1 \\
\end{tabular}
\end{center}
\end{table}

\section{Runtype 2 description}
\begin{table}
\begin{center}
\begin{tabular}{ l l }
runtype & Keyword to determine runtype. Can be 1 or 2. \\
        & I don't want to hardcode this description twice...\\
param_2 & Keyword to determine the parameter of runtype = 2 \\
\end{tabular}
\end{center}
\end{table}
  • 3
    Welcome to TeX.SE! – Mensch Nov 27 '18 at 21:11
3

Below, I'm defining the following commands:

  • \setkeyword{<keyword>}{<description>} can be used to declare a keyword.
  • \getkeyword{<keyword>} retrieves the description you provided.
  • \keywordtable{<key1>,<key2>,…} displays a table containing the descriptions corresponding to a comma-separated list of keywords.

(Note that keywords containing underscores are allowed.)

\documentclass{article}

\usepackage[T1]{fontenc} %% <- necessary for _ to be displayed correctly
\usepackage{etoolbox}    %% <- for \csuse, \csdef, \ifcsdef and \forcsvlist

%% Define/retrieve a new keyword:
\newcommand\setkeyword[2]{\csdef{keyw@#1}{#2}}
\newcommand*\getkeyword[1]{%
  \ifcsdef{keyw@#1}{%                %% <- if the key is defined...
    \csuse{keyw@#1}%                 %% <- return the description
  }{%                                %% <- otherwise...
    ??%                              %% <- question marks
    %\errmessage{Undefined key: #1}% %% <- or an ERROR, if you prefer
  }%
}

%% Display a table describing a list of keywords:
\newcommand*\keywordtable[1]{%
  \begin{tabular}{lp{8cm}}
    \forcsvlist{\tableentry}{#1} %% <- apply \tableentry to each value in #1
  \end{tabular}%
}
\newcommand*\tableentry[1]{%
    \formattableentry{\detokenize{#1}}{\getkeyword{#1}}%
}
\newcommand\formattableentry[2]{ #1 & #2 \\ }

% %% Declaration of keywords:
\setkeyword{runtype}{Keyword to determine runtype. Can be 1 or 2. \par
                     I don't want to hardcode this description twice\ldots}
\setkeyword{param_1}{Keyword to determine the parameter of runtype = 1}
\setkeyword{param_2}{Keyword to determine the parameter of runtype = 2}

\begin{document}

\section{Runtype 1 description}
\keywordtable{runtype,param_1}

\section{Runtype 2 description}
\keywordtable{runtype,param_2}

\end{document}

output


Some remarks

  • \setkeyword{<keyword>}{<description>} effectively defines \keyw@<keyword> for you, so that it expands to <definition>. You can't use this macro directly because its name contains an @.
  • \getkeyword{<keyword>} just calls \keyw@<keyword>.
  • I'm using the e-TeX primitive \detokenize to print the keyword. This command strips all tokens in its argument of their special meaning (by changing their catcodes), so you can for instance use \detokenize{param_1} safely.
  • Without \usepackage[T1]{fontenc}, underscores are displayed as " ̇", rather than as "_". You'll probably want to use this package anyway because of the reasons outlined here.
  • I'm using tabularx to create a table that has the same width as the current line width. You can replace \linewidth by some other value if you want a different width (or just use tabular, in which case you should replace the X column type by something else).


A more customisable version

By request, here is the most customisable version I can think of. This lets you create keywords using \setkeyword{<keyword>}{<key1>=<value1>,<key2>=<value2>,…} and retrieve them using \getkeyword{<keyword>}{<key>}. You can create a table with rows corresponding a set of keywords and columns corresponding to specific keys using

\keywordtable[<key1>,<key2>,…]{<keyword1>,<keyword2>,…}

I'm using pkgkeys, which is documented in section 82 of the pgf manual.

\documentclass{article}

\usepackage[T1]{fontenc} %% <- necessary for _ to be displayed correctly
\usepackage{pgfkeys}     %% <- for everything starting with \pgf
\usepackage{etoolbox}    %% <- for \forcsvlist

\newcommand*{\declarekeyword}[1]{%
  \pgfkeys{
    /keyw/#1/.is family,
    /keyw/#1/.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial={##1}},
  }%
}
\newcommand\setkeyword[2]{%
  \declarekeyword{#1}%
  \pgfkeys{/keyw/#1/.cd,name=\detokenize{#1},#2}%
}
\newcommand*\getkeyword[2]{\pgfkeysvalueof{/keyw/#1/#2}}

%% Display a table describing a list of keywords:
\newcommand*\keywordtable[2][name,description]{%
  \begin{center}
    \begin{tabular}{\forcsvlist{\getkeyword{@alignment}}{#1}}
      \forcsvlist{\tableentry[\bfseries]{#1}}{@headings}
      \forcsvlist{\tableentry{#1}}{#2}
    \end{tabular}
  \end{center}%
}
\newcommand*\tableentry[3][]{%
    \let\keywcolsep\empty
    \forcsvlist{\keywcolsep\def\keywcolsep{&}\formattableentry[#1]{#3}}{#2}\\
}
\newcommand*\formattableentry[3][]{#1{\getkeyword{#2}{#3}}}

%% "Fake" keywords (control column titles and alignment)
\setkeyword{@headings}{name=Parameter,description=Description,value=Value}
\setkeyword{@alignment}{name=l,description=l,value=r}

%% Declaration of keywords:
\setkeyword{runtype}{description=A parameter,value=1}
\setkeyword{param_1}{description=Another parameter,value=0}
\setkeyword{param_2}{name=\detokenize{PARAM_2},description=A third parameter,value=42}

\begin{document}

\section{Runtype 1 description}
\keywordtable{runtype,param_1}

\section{Runtype 2 description}
\keywordtable[name,value,description]{runtype,param_2}

\end{document}

output

|improve this answer|||||
  • Awesome! This is a very versatile method and thorough response, thank you very much! It is a bit complex and wordy (at least in setup) for something that feels conceptually simple, but apparently that's just how latex is. Not sure what's the difference between tabular and tabularx, but the later didn't compile correctly for me so went back to the former. – Nordico Nov 29 '18 at 2:27
  • 1
    @Nordico \begin{tabularx}{\linewidth}{lX} creates a table of total width \linewidth whose second column stretches to accommodate. It was very much inessential to the answer, so I probably shouldn't have used it. (I needed to use either the p{<some width>} or the X column type because one of the entries spans two lines.) – Circumscribe Nov 29 '18 at 9:04
  • 1
    It is 1-1, but you could for instance have setkeyword call \csdef twice to make it 1-2, like I've done here. I can think of a few other (fancier) ways to do this, but those wouldn't really perform any better than this. – Circumscribe Dec 4 '18 at 21:45
  • 1
    @Nordico See the bottom part of my answer for a ridiculously customisable version. – Circumscribe Dec 5 '18 at 9:17
  • 1
    @Nordico You can use description={something containing = and ,.}. This actually didn't work because I had forgotten to wrap the ##1 in the /keyw/#1/.unknown/.style = … line by {}. (Fixed now) – Circumscribe Dec 5 '18 at 18:58
2
\documentclass[12pt]{article}
\newcommand\zzz{%
  runtype & Keyword to determine runtype. Can be 1 or 2. \\
          & I don't want to hardcode this description twice...}
\begin{document}
\section{Runtype 1 description}
\begin{table}[ht]
\begin{center}
\begin{tabular}{ l l }
\zzz\\
param\_1 & Keyword to determine the parameter of runtype = 1 \\
\end{tabular}
\end{center}
\end{table}

\section{Runtype 2 description}
\begin{table}[ht]
\begin{center}
\begin{tabular}{ l l }
\zzz\\
param\_2 & Keyword to determine the parameter of runtype = 2 \\
\end{tabular}
\end{center}
\end{table}
\end{document}

enter image description here

|improve this answer|||||
  • Ok, this works but feels like using a cannon to kill a fly. More precisely: I need to use this with around a hundred keywords, is defining a hundred newcommand to use as a "reference dictionary" advisable? How can I use descriptive names (tablekey_keyname) without using underscores? – Nordico Nov 27 '18 at 21:20
  • @Nordico As to how to use underscores in a command name, options are limited: 1) change catcode of _ (not recommended, as it breaks subscript math), or 2) \csdef and \csuse with the etoolbox package (recommended if you insist on underscore in name) – Steven B. Segletes Nov 27 '18 at 21:24
  • I mean, not necessarily an underscore, it can be other symbol ("tablekey.keyname") or a way of structuring the commands (like an equivalent to C++ namespaces, so I could do "tablekey::keyname"), anything that makes it more human readable/usable than just altogether "tablekeykeyname". – Nordico Nov 27 '18 at 21:29
  • 1
    @Nordico In my example, add \catcode`:=11 as the first line after \documentclass, This changes : to be a "letter". Then \newcommand\zz:z{...} can be defined and later used as \zz:z. What I don't know is if this breaks anything (e.g., spacing, hyphenation), if you use : as a normal character. – Steven B. Segletes Nov 27 '18 at 21:36
  • 1
    I thought \zzz was reserved for @David Carlisle ;-) – Raoul Kessels Nov 28 '18 at 11:12
1

It depends on where you want to first key the description you want to repeat.

Probably the best place is in the document preamble or in an external file that you can \input.

\documentclass{article}
\usepackage{xparse}

% a few line of code for setting up the system
\ExplSyntaxOn

\NewDocumentCommand{\newdesc}{mm}
 {% #1 is a key, #2 is the description
  \prop_gput:Nnn \g_nordico_descriptions_plist { #1 } { #2 }
 }

\NewDocumentCommand{\getdesc}{m}
 {% #1 is a key
  \prop_if_in:NnTF \g_nordico_descriptions_plist { #1 }
   { \prop_item:Nn \g_nordico_descriptions_plist { #1 } }
   { ???~non~existent~description~??? }
 }

\prop_new:N \g_nordico_descriptions_plist

\ExplSyntaxOff

% the descriptions (they can go in an external file
% say desc.tex and here you'd do \input{desc}

\newdesc{A}{I don't want to hardcode this description twice...}

\begin{document}

\section{Runtype 1 description}
\begin{center}
\begin{tabular}{ l l }
runtype  & Keyword to determine runtype. Can be 1 or 2. \\
         & \getdesc{A} \\
param\_1 & Keyword to determine the parameter of runtype = 1 \\
\end{tabular}
\end{center}

\section{Runtype 2 description}
\begin{center}
\begin{tabular}{ l l }
runtype  & Keyword to determine runtype. Can be 1 or 2. \\
         & \getdesc{A} \\
param\_2 & Keyword to determine the parameter of runtype = 2 \\
\end{tabular}
\end{center}

\end{document}

Note that it is not at all necessary to place a tabular in a floating table environment (which might make the tabular go to another page).

enter image description here

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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