2

This is somewhat of an expansion of the previous question:Macro to apply a custom command across a comma-separated list

I'm working on creating a garden journal in LaTex. I have a .csv database file of plants (db-1.csv) from which I create subset tables for each area of my garden using conditions from the datatool package, which has been working great.

I would like to index the plants that are pulled from the database in each of my tables. I am currently using the makeidx package. This way, my final index will include where a particular plant was mentioned even if the mention occurred in a table and not in the text directly.

How can I make my apply macro work on the results of my datatool conditions (\theback)?

Here's my MWE:

\documentclass[12pt]{book} 
\usepackage{datatool}
\usepackage{colortbl}
%necessary to color a table

\definecolor{lightmauve}{rgb}{0.86, 0.82, 1.0}

\usepackage{makeidx} % Required to make an index
\makeindex % Tells LaTeX to create the files required for indexing



% This command is for applying a single command to a comma-separated list of tokens, and listing the results separated by ", " (I removed the ``,'').
\makeatletter
\newcommand{\apply}[3][]{
% #1: optional separator to print between applications; default=[, ]
% #2: command to apply; 
% #3: list to apply command to 
  \def\itemsep{\def\itemsep{#1}} % first call to \itemsep prints nothing; later calls print #1
  \def\zz{\itemsep#2}%
  \@for \listelement:=#3\do{\expandafter\zz\expandafter{\listelement}}%
}
\makeatother

%%%%%%%%%%%%%%%%%%%


\DTLloaddb{dbsample}{db-1.csv}


\begin{document}

%First I'm creating my custom comma-separated list, the result of datatool conditions.  Can be called using \theback

\def\theback{\DTLforeach*[\DTLisSubString{\Location}{back}]{dbsample}%
{\Name=Name,\Variety=Variety,\Location=Location,\Established=Established}%
{\Name!\Variety, }}

\vspace{10mm}

%%% Now ideally I'd like to have 
%\apply{\index}{\theback} 
%to work in my table, but it does not.  This did not work, I may have to figure out how to set a macro (or definition?) within a macro???


%Next, this is my table.

\begin{table}[htbp] 
\caption{Plants that are pre-existing are highlighted in \colorbox{lightmauve}{lightmauve}} 
\vspace{5mm}
\centering 
\begin{tabular}{lllr} 
\bfseries Name & 
\bfseries Variety&
\bfseries Location & 
\bfseries Established % 
\DTLforeach*[\DTLisSubString{\Location}{back}]{dbsample}%
{\Name=Name,\Variety=Variety,\Location=Location,\Established=Established}
{% 
\DTLifSubString{\Established}{yes}{\\\rowcolor{lightmauve}}{\\\rowcolor{white}}%
\Name & \Variety & \Location & \Established}
\end{tabular} 

\apply{\index}{Daffodil!apricot,St. John's Wort}
%the above will work, but what I'm hoping for is instead, something along the lines of:
%\apply{\index}{\theback}

\end{table}


\printindex % Output the index

\end{document}

Sorry, I'm not sure if I'm referring to things with the correct names. Any suggestions or help would be greatly appreciated. Thank you so much!

1
  • 1
    So my comment now is obsolete, thus let's delete obsolete comments. ;-) – Ulrich Diez Jun 23 '20 at 19:18
1

As you can see in the answer to the question How to change the space between the itemize “items” in LaTeX?, \itemsep is already defined in LaTeX.

If you wish, you can combine the exchanging of non-delimited macro-arguments with good old \romannumeral-expansion for defining a macro which checks the toplevel-expansion of \Variety for emptiness and then appends the things needed for an index-entry to a comma-list-macro (called \theback)—I tried to explain \romannumeral-expansion in my answer to the question How can I know the number of expandafters when appending to a csname macro?.

In the example below I decided to go the \xdef...{\unexpanded{...}}-route in order to have \unexpanded work against LaTeX's halving of the amount of consecutive hashes with each macro-expansion. Take this for a moot point. ;-)

\documentclass[12pt]{book} 
\usepackage{datatool}
\usepackage{colortbl}
%necessary to color a table

\definecolor{lightmauve}{rgb}{0.86, 0.82, 1.0}

\usepackage{makeidx} % Required to make an index
\makeindex % Tells LaTeX to create the files required for indexing

\makeatletter

%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Exchange two arguments:
%%.............................................................................
\newcommand\exchangeargs[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Append comma-delimited element to comma-list-macro holding index-entries:
%%.............................................................................
\newcommand\GlobalAppendIndexEntryToCommalist[5]{%
  % #1 = comma-list-macro where tokens/index-entries are to be appended
  % #2 = !
  % #3 = ,
  % #4 = \Name
  % #5 = \Variety
  \xdef#1{%
    \unexpanded\expandafter{%
      \romannumeral0%
      \expandafter\exchangeargs\expandafter{\expandafter{%
        \romannumeral0%
        \expandafter\CheckWhetherNull\expandafter{#5}{%
          \expandafter\exchangeargs\expandafter{#4}{ }%
        }{%
          \expandafter\exchangeargs\expandafter{#5}{%
            \exchangeargs{#2}{%
              \expandafter\exchangeargs\expandafter{#4}{ }%
            }%
          }%
        }%
      }}{%
        \expandafter\CheckWhetherNull\expandafter{#1}{ }{%
          \expandafter\exchangeargs\expandafter{#1#3}{ }%
        }%
      }%
    }%
  }%
}%

\newcommand*\theback{}%
\newcommand*\GlobalResettheback{\gdef\theback{}}%

% This macro applies a command to each element of a list of comma-separated arguments:
\newcommand{\apply}[2]{%
  % #1: command to apply; 
  % #2: (macro holding) list of comma-separated arguments;
  \@for\listelement:=#2\do{%
    \expandafter\exchangeargs\expandafter{\expandafter{\listelement}}{#1}%
  }%
}%

\makeatother

%%%%%%%%%%%%%%%%%%%

\DTLloaddb{dbsample}{db-1.csv}

\begin{document}

%This is my table.

\begin{table}[htbp]
\caption{Plants that are pre-existing are highlighted in \colorbox{lightmauve}{lightmauve}}
\vspace{5mm}%
\centering 
\GlobalResettheback
\begin{tabular}{lllr} 
\bfseries Name & 
\bfseries Variety&
\bfseries Location & 
\bfseries Established %
\DTLforeach*[\DTLisSubString{\Location}{back}]{dbsample}%
{\Name=Name,\Variety=Variety,\Location=Location,\Established=Established}
{%
  \DTLifSubString{\Established}{yes}{\\\rowcolor{lightmauve}}{\\\rowcolor{white}}%
  \Name & \Variety & \Location & \Established%
  \GlobalAppendIndexEntryToCommalist{\theback}{!}{,}{\Name}{\Variety}%
}%
\end{tabular} 
%
%\apply{\index}{Daffodil!apricot,St. John's Wort}
%\show\theback
\apply{\index}{\theback}
%
% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
% Be aware: 
%
% If you decide to have tables not as floating objects but as part of the 
% main text and switch to tabularx for having multipage-tables, then 
% carrying out all \index-commands _after_ already having shipped all pages
% of the table may yield wrong page-numbers with index-entries of things
% that do not occur on the last page of the table but do occur on pages
% prior to that last page.
%
% That's why below I added a suggestion where index-entries are produced
% and \index-commands are carried out at the time of producing the
% table-rows via \DTLforeach*.
% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
%
\end{table}

\printindex % Output the index

\end{document}

Probably you don't need to maintain such a comma-list-macro but can have LaTeX construct the index-entry from within the \DTLforeach*-loop directly:

\documentclass[12pt]{book} 
\usepackage{datatool}
\usepackage{colortbl}
%necessary to color a table

\definecolor{lightmauve}{rgb}{0.86, 0.82, 1.0}

\usepackage{makeidx} % Required to make an index
\makeindex % Tells LaTeX to create the files required for indexing

\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Exchange two arguments:
%%.............................................................................
\newcommand\exchangeargs[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Construct index-entry from \Name and \Variety
%%.............................................................................
\newcommand\ConstructIndexentry[4]{%
  % #1 = indexing-command, usually \index
  % #2 = !
  % #3 = \Name
  % #4 = \Variety
  \expandafter\exchangeargs\expandafter{\expandafter{%
    \romannumeral0%
    \expandafter\CheckWhetherNull\expandafter{#4}{%
      \expandafter\exchangeargs\expandafter{#3}{ }%
    }{%
      \expandafter\exchangeargs\expandafter{#4}{%
        \exchangeargs{#2}{%
          \expandafter\exchangeargs\expandafter{#3}{ }%
        }%
      }%
    }%
  }}{#1}%
}%
\makeatother

\DTLloaddb{dbsample}{db-1.csv}

\begin{document}

%This is the table.

\begin{table}[htbp]
\caption{Plants that are pre-existing are highlighted in \colorbox{lightmauve}{lightmauve}}
\vspace{5mm}%
\centering 
\begin{tabular}{lllr} 
\bfseries Name & 
\bfseries Variety&
\bfseries Location & 
\bfseries Established %
\DTLforeach*[\DTLisSubString{\Location}{back}]{dbsample}%
{\Name=Name,\Variety=Variety,\Location=Location,\Established=Established}
{%
  \DTLifSubString{\Established}{yes}{\\\rowcolor{lightmauve}}{\\\rowcolor{white}}%
  \Name & \Variety & \Location & \Established%
  \ConstructIndexentry{\index}{!}{\Name}{\Variety}%
}%
\end{tabular} 
\end{table}

\printindex % Output the index

\end{document}
0

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.