2

Please see the following example. I thought that this would be pretty straightforward. But the processing seems to be delayed (or done in chunks) where erroneous results are being produced.

\documentclass{article}

\usepackage{filecontents}

\usepackage{datatool}

\begin{document}

\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}


% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}

% Define macros
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
  \expandafter\def\csname\Tag\endcsname{\N}
  \expandafter\def\csname\Tag Value\endcsname{\V}}


\begin{enumerate}
\item \b, \bValue               % Should give B, 200
\item \d, \dValue               % D, 400
\item \i, \iValue               % I, 900
\end{enumerate}

\end{document}

See the results produced.

enter image description here

How can I make the processing immediate?

1
  • 1
    If you know that your \N and \V values are simple (work in a \edef}, you can use \edef instead of \def. You shouldn't define commands like \b or \i: you are overwriting existing latex commands here. – Ulrike Fischer Nov 6 '19 at 16:38
1

You need to use \edef, otherwise \N and \V would print the last value assigned to them.

Anyway, you should be very careful when using \def; for instance you're redefining \a, \b and \c, which are important LaTeX commands for typesetting accented letters.

I suggest a different strategy: first do the processing of the database inside a group and use \xdef (global expanded definition), but using “internal commands” that you can recover with “external” ones.

\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}

\documentclass{article}
\usepackage{datatool}

\newcommand{\getrowN}[1]{\csname masroor@row@#1@N\endcsname}
\newcommand{\getrowV}[1]{\csname masroor@row@#1@V\endcsname}

% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}

% Define macros
\begingroup
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
  \expandafter\xdef\csname masroor@row@\Tag@N\endcsname{\N}%
  \expandafter\xdef\csname masroor@row@\Tag@V\endcsname{\V}%
}
\endgroup

\begin{document}

\begin{enumerate}
\item \getrowN{b}, \getrowV{b}  % Should give B, 200
\item \getrowN{d}, \getrowV{d}  % D, 400
\item \getrowN{i}, \getrowV{i}  % I, 900
\end{enumerate}

\end{document}

enter image description here

By using a group, the fact that you're using \def for \Tag, \N or \V will not propagate outside the group and if those commands are already defined, they will revert their behavior at \endgroup.

It would be different for \b or \c, because you'd need them in the document. That's the reason for using \getrowN and \getrowV in order to retrieve the values.

1
  • Thanks. The usage part of the macros is already written and is an extensive one. So, to adapt your suggested method, which is an excellent one (as always) we would need a major rework in a large document. I went for writing the definitions in an external style file and using that file after the output stream is closed. It is working fine. I will accept your answer anyway, because as of this moment, this is the only (good) one. (Those \a, \b... were only part of a quick MWE, actual usages are very different, but thanks for the word of caution.) – Masroor Nov 6 '19 at 23:12
1

You almost did it.

But...

Issue 1:

The definitions of \Tag, \N and \V will be overridden with each iteration of the \DTLforeach-loop.

Therefore these macros need to be expanded with each iteration of the loop.

\csname triggers expanding things (and thus in your example triggers expansion of \Tag) until finding the matching \endcsname.

But with \N and \V some extra steps are needed for ensuring not to have the token \N/not to have the token \V but to have the expansion of the token \N/to have the expansion of the token \V within the definition-text within each iteration of the loop.

I slightly modified your example to show that on the basis of \csname's triggering of expansion you can use \expandafter for "tricking" LaTeX into expanding \N and \V as well:

\documentclass{article}

\usepackage{filecontents}

\usepackage{datatool}

\begin{document}

\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}


% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}

% Define macros
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
  \expandafter\def\csname\Tag\expandafter\endcsname\expandafter{\N}%
  \expandafter\def\csname\Tag Value\expandafter\endcsname\expandafter{\V}%
}%


\begin{enumerate}
\item \a, \aValue
\item \b, \bValue
\item \c, \cValue
\item \d, \dValue
\item \e, \eValue
\item \f, \fValue
\item \g, \gValue
\item \h, \hValue
\item \i, \iValue
\end{enumerate}

\end{document}

Issue 2:

\a, \b, \c, \d and \i are already defined in the LaTeX 2ε-kernel.
The \DTLforeach-loop might override the already existing definitions.

Therefore I once more modified your example:

  • Each command that is to be defined has the name-prefix MY.
  • Instead of \def now \newcommand* is used. This will raise an error-message in case of overriding something already defined.
  • The usefulness of the macro \name is shown:

    \name\newcommand{macro}...\newcommand\macro...
    \name\string{macro}\string\macro
    \name\name\let{macroA}={macroB}\name\let\macroA={macroB}\let\macroA=\macroB

 

\documentclass{article}

\newcommand\exchange[2]{#2#1}
\csname @ifdefinable\endcsname\name{\long\def\name#1#{\romannumeral0\innername{#1}}}%
\newcommand\innername[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%

\usepackage{filecontents}

\usepackage{datatool}

\begin{document}

\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}

% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}

% Define macros
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
  \name\expandafter\newcommand\expandafter*\expandafter{MY\Tag}\expandafter{\N}%
  \name\expandafter\newcommand\expandafter*\expandafter{MY\Tag Value}\expandafter{\V}%
}%

\begin{enumerate}
\item \MYa, \MYaValue
\item \MYb, \MYbValue
\item \MYc, \MYcValue
\item \MYd, \MYdValue
\item \MYe, \MYeValue
\item \MYf, \MYfValue
\item \MYg, \MYgValue
\item \MYh, \MYhValue
\item \MYi, \MYiValue
\end{enumerate}

\end{document}

Besides this I can offer a macro \ExtractKthArg which extracts the K-th argument of a list of non-delimited arguments if a K-th argument exists in the list.

You can have \DTLforeach define for each row of the database a macro which expands to a list of non-delimited arguments denoting the entries in the columns with that row, and use \ExtractKthArg for retrieving the single elements/column-entries of the row:

\documentclass{article}

\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, 
%%    \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@PassFirstBehindThirdToSecond[3]{#2{#3}{#1}}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@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\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%%               {<list of non-delimited args>}%
%%               {<tokens in case K is not positive>}%
%%               {<tokens in case list has less than K args>}%
%% 
%% In case K is not positive:
%%   Does deliver <tokens in case K is not positive>.
%% In case K is positive but there is no K-th argument in <list of non-delimited args> : 
%%   Does deliver <tokens in case list has less than K args>.
%% In case there is a K-th argument in <list of non-delimited args> : 
%%   Does deliver that K-th argument with one level of braces removed.
%%
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" with \expandafter.
%%
%% Examples:
%%
%%   \ExtractKthArg{0}{ABCDE}{K not positive}{No Kth Element in list} yields: K not positive
%%
%%   \ExtractKthArg{3}{ABCDE}{K not positive}{No Kth Element in list} yields:  C
%%
%%   \ExtractKthArg{3}{AB{CD}E}{K not positive}{No Kth Element in list} yields:  CD
%%
%%   \ExtractKthArg{4}{{001}{002}{003}{004}{005}}{K not positive}{No Kth Element in list} yields: 004
%%
%%   \ExtractKthArg{6}{{001}{002}{003}}{K not positive}{No Kth Element in list} yields: No Kth Element in list
%% 
%%=============================================================================
\newcommand\ExtractKthArg[1]{%
  % #1: <integer number K>
  \romannumeral0%
  \expandafter\UD@ExtractKthArgCheck
  \expandafter{\romannumeral\number\number#1 000}%
}%
\newcommand\UD@ExtractKthArgCheck[4]{%
  % #1: <Amount of letters "m" corresponding to number K>
  % #2: <list of non-delimited args>
  % #3: <tokens in case K is not positive>
  % #4: <tokens in case list has less than K args>
  \UD@CheckWhetherNull{#1}{ #3}{%
    \expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#4}%
  }%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
  % #1: <remaining amount of letters "m">
  % #2: <(remaining) list of non-delimited args>
  % #3: <tokens in case list has less than K args>
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#2{}.}{ #3%
  }{%
    \UD@CheckWhetherNull{#1}{%
      \UD@ExtractFirstArgLoop{#2\UD@SelDOm}%
    }{%
      \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#2}%
      {\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}}%
      {#3}%
    }%
  }%
}%
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  % #1: <(remaining) list of non-delimited args>
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@firstoftwo{\expandafter}{} \UD@secondoftwo{}#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%% End of code for \ExtractKthArg.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatother

\usepackage{filecontents}

\usepackage{datatool}

\begin{document}

\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}

% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}

\makeatletter
% Define macro for each row:
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
   \expandafter\@gobble\string{%
      \expandafter\UD@PassFirstBehindThirdToSecond\expandafter{\V}{%
        \expandafter\UD@PassFirstBehindThirdToSecond\expandafter{\N}{%
          \expandafter\UD@PassFirstBehindThirdToSecond\expandafter{\Tag}{%
            \UD@secondoftwo{}%
          }%
        }%
      }%
      {\expandafter\newcommand\csname Row\Tag%
      \expandafter\expandafter\expandafter\endcsname
      \expandafter\expandafter\expandafter{\expandafter\@gobble\string}}%
   }%
   %\expandafter\show\csname Row\Tag\endcsname
}%
\newcommand\GenericGetElementOfRow[4]{%
  % #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
  % #2 = Column-Number (beginning with 1)
  % #3 = <Tokens in case row is undefined>
  % #4 = <Tokens in case row is defined but does not have an element in column #2>
  \@ifundefined{Row#1}{%
    #3%
  }{%
    \expandafter\expandafter\expandafter\UD@PassFirstToSecond
    \expandafter\expandafter\expandafter{\csname Row#1\endcsname}%
    {\ExtractKthArg{#2}}%
    {}{#4}%
  }%
}%
\newcommand\GetTagofRow[1]{%
  % #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
  \GenericGetElementOfRow{#1}{1}{%
    % Tokens in case row is undefined:
    \nfss@text{\reset@font\bfseries??}%
  }{%
    % Tokens in case row defined but does not have K-th element:
    \nfss@text{\reset@font\bfseries??}%
  }%
}%
\newcommand\GetNofRow[1]{%
  % #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
  \GenericGetElementOfRow{#1}{2}{%
    % Tokens in case row is undefined:
    \nfss@text{\reset@font\bfseries??}%
  }{%
    % Tokens in case row defined but does not have K-th element:
    \nfss@text{\reset@font\bfseries??}%
  }%
}%
\newcommand\GetVofRow[1]{%
  % #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
  \GenericGetElementOfRow{#1}{3}{%
    % Tokens in case row is undefined:
    \nfss@text{\reset@font\bfseries??}%
  }{%
    % Tokens in case row defined but does not have K-th element:
    \nfss@text{\reset@font\bfseries??}%
  }%
}%
\makeatother

\begin{itemize}
\item[\GetTagofRow{a}:] \GetNofRow{a}, \GetVofRow{a}
\item[\GetTagofRow{b}:] \GetNofRow{b}, \GetVofRow{b}
\item[\GetTagofRow{c}:] \GetNofRow{c}, \GetVofRow{c}
\item[\GetTagofRow{d}:] \GetNofRow{d}, \GetVofRow{d}
\item[\GetTagofRow{e}:] \GetNofRow{e}, \GetVofRow{e}
\item[\GetTagofRow{f}:] \GetNofRow{f}, \GetVofRow{f}
\item[\GetTagofRow{g}:] \GetNofRow{g}, \GetVofRow{g}
\item[\GetTagofRow{h}:] \GetNofRow{h}, \GetVofRow{h}
\item[\GetTagofRow{i}:] \GetNofRow{i}, \GetVofRow{i}
\item[\GetTagofRow{j}:] \GetNofRow{j}, \GetVofRow{j}
\end{itemize}

\end{document}

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.