I want to have some condition of what to do if the key that was provided is not in the db. But \DTLfetch does return a value in case it had found one before. How can I catch all the 'x' in this example?

\documentclass{article}

\usepackage{datatool}

% csv file 1
\begin{filecontents*}{abbreviationss.csv}
term, abbreviation
Machine Learning, ML
Artificial Neural Network, ANN
\end{filecontents*}

\begin{document}

\begin{itemize}
\item x : \DTLfetch{abbreviations}{term}{x}{abbreviation}
\item Machine Learning : \DTLfetch{abbreviations}{term}{Machine Learning}{abbreviation}
\item x : \DTLfetch{abbreviations}{term}{x}{abbreviation}
\item Artificial Neural Network : \DTLfetch{abbreviations}{term}{Artificial Neural
Network}{abbreviation}
\item x : \DTLfetch{abbreviations}{term}{x}{abbreviation}
\end{itemize}

\end{document}


output:

I think the most easy approach is using \DTLgetvalueforkey (and evaluating/calling the macro defined by \DTLgetvalueforkey):

\documentclass{article}

\usepackage{datatool}

\begin{filecontents*}{abbreviationss.csv}
"term", "abbreviation"
Machine Learning, ML
Artificial Neural Network, ANN
\end{filecontents*}

\newcommand{\getabbreviation}[1]{%
#1 : %
\DTLgetvalueforkey{\scratchmacro}{abbreviation}{abbreviations}{term}{#1}%
\scratchmacro
%\scratchmacro will be \@dtlnovalue -> "Undefined Value" if #1 is not found.
% Alternatively:
% \DTLifnull{\scratchmacro}{Hey, this is undefined!}{\scratchmacro}%
}%

\begin{document}

\begin{itemize}
\item\getabbreviation{x}
\item\getabbreviation{Machine Learning}
\item\getabbreviation{x}
\item\getabbreviation{Artificial Neural Network}
\item\getabbreviation{x}
\end{itemize}

\end{document}


Like the approach of Simon Dispa this is based on \DTLforeach*:

\documentclass{article}

\usepackage{datatool}

\begin{filecontents*}{abbreviationss.csv}
"term", "abbreviation"
Machine Learning, ML
Artificial Neural Network, ANN
\end{filecontents*}

\newif\ifnotfound
\global\notfoundtrue

\newcommand{\getabbreviation}[1]{%
#1 : %
\global\notfoundtrue
\DTLforeach*[{\equal{\term}{#1}}]{abbreviations}{\abbreviation=abbreviation, \term=term}%
{\abbreviation\global\notfoundfalse\dtlbreak}%
\ifnotfound Undefined Value\fi
}%

\begin{document}

\begin{itemize}
\item\getabbreviation{x}
\item\getabbreviation{Machine Learning}
\item\getabbreviation{x}
\item\getabbreviation{Artificial Neural Network}
\item\getabbreviation{x}
\end{itemize}

\end{document}


If the real-life-scenario is not just about some itemize-thing but you really need to catch the situation of \DTLfetch not finding the ⟨column1 value⟩:

With your MWE you get a lot of ! Package datatool Error: No row found in database abbreviations' for column  1' matching x'.-errors on the terminal and in the .log-file.

Syntax of \DTLfetch is:

\DTLfetch{⟨db name⟩}%
{⟨column1 name⟩}%
{⟨column1 value⟩}%
{⟨column2 name⟩}%

Section "6.11.1 Operating on Current Row" of the User Manual for datatool bundle version 2.32 says:

\dtlgetrowindex{⟨row cs⟩}{⟨db name⟩}{⟨col idx⟩}{⟨value⟩}
Similar to \DTLgetrowindex but doesn't produce an error if no match is found. You can test the result by using \ifx⟨row cs⟩\dtlnovalue.

So you can write a wrapper for \DTLfetch which applies

\dtlgetrowindex{⟨row cs⟩}%
{⟨db name⟩}%
{⟨col idx of column1⟩}%
{⟨column1 value⟩}%

for defining ⟨row cs⟩ and then does:

\ifx⟨row cs⟩\dtlnovalue\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
⟨ tokens in case ⟨column1 name⟩ does not have a value ⟨column1 value⟩ ⟩%
}{%
\DTLfetch{⟨db name⟩}{⟨column1 name⟩}{⟨column1 value⟩}{⟨column2 name⟩}%
}%

You need the index of column1/of ⟨col idx of column1⟩.

You can get that via \dtlcolumnindex{⟨db name⟩}{⟨column1 name⟩}

In order to have the arguments of \dtlgetrowindex—especially ⟨col idx of column1⟩=\dtlcolumnindex{⟨db name⟩}{⟨column1 name⟩}—expanded before carrying out \dtlgetrowindex, you can define a scratch-macro via \protected@edef where expansion of the command \dtlgetrowindex and expansion of the to-be-defined command ⟨row cs⟩ is prevented by prepending \noexpand, but the expansion of other arguments of \dtlgetrowindex is not prevented, and then call that scratch-macro:

\documentclass{article}

\usepackage{datatool}

%\DTLfetchifcolOneValueExists{<db name>}%
%                            {<column1 name>}%
%                            {<column1 value>}%
%                            {<column2 name>}%
%                            {<tokens in case <column1-value> does not exist>}%
%
%   Turn @ into a normal letter so that it can be used as part of the name of a control sequence:
\makeatletter
%   Test if the command \NewDocumentCommand is not available.
%   If so, use traditional \newcommand.  Otherwise use it.
\@ifundefined{NewDocumentCommand}%
{\newcommand\DTLfetchifcolOneValueExists[5]}%
{\NewDocumentCommand\DTLfetchifcolOneValueExists{mmmmm}}%
{%
% open a local scope:
\begingroup
% define the scratch macro
\protected@edef\@dtl@dogetrowforvalue{%
\endgroup\noexpand\dtlgetrowindex{\noexpand\dtl@rowidx}{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
}%
% Call the scratch-macro:
\@dtl@dogetrowforvalue
% Expansion of the scratch-macro yields:
%   \endgroup %<-after processing \endgroup the local scope is closed and thus the scratch-macro is undefined.
%   \dtlgetrowindex{\dtl@rowidx}{<expansion of #1>}{<expansion of \dtlcolumnindex{#1}{#2}>}{<expansion of #3>}%
%   % After processing \dtlgetrowindex the macro \dtl@rowidx is defined.
\ifx\dtl@rowidx\dtlnovalue\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{#5}{\DTLfetch{#1}{#2}{#3}{#4}}%
}%
\makeatother

% csv file 1
\begin{filecontents*}{abbreviationss.csv}
term, abbreviation
Machine Learning, ML
Artificial Neural Network, ANN
\end{filecontents*}

\begin{document}

\begin{itemize}
\item x :
\DTLfetchifcolOneValueExists{abbreviations}%
{term}%
{x}%
{abbreviation}%
{Column term' doesn't have a value x'}
\item Machine Learning :
\DTLfetchifcolOneValueExists{abbreviations}%
{term}%
{Machine Learning}%
{abbreviation}%
{Column term' doesn't have a value Machine Learning'}
\item x :
\DTLfetchifcolOneValueExists{abbreviations}%
{term}%
{x}%
{abbreviation}%
{\DTLstringnull}
\item Artificial Neural Network :
\DTLfetchifcolOneValueExists{abbreviations}%
{term}%
{Artificial Neural Network}%
{abbreviation}%
{Column term' doesn't have a value Artificial Neural Network'}
\item x :
\DTLfetchifcolOneValueExists{abbreviations}%
{term}%
{x}%
{abbreviation}%
{\dtlnovalue}
\end{itemize}

\end{document}


If you wish to obtain some null-value instead of the phrase "Column term' doesn't have a value x' ", you can provide one of the tokens \DTLstringnull/\DTLnumbernull/\dtlnovalue instead. (\DTLstringnull yields NULL. \DTLnumbernull yields 0. \dtlnovalue yields Undefined Value. All three tokens can be used as first argument of \DTLifnull etc.)

The command \itemx will generate a new \item every time a term is found. Otherwise it does nothing.

Output:

\documentclass{article}

\usepackage{datatool}

\begin{filecontents*}{abbreviationss.csv}
"term", "abbreviation"
Machine Learning, ML
Artificial Neural Network, ANN
\end{filecontents*}

\begin{document}

\DTLforeach*{abbreviations}{\abbreviation=abbreviation, \term=term}
{\ifthenelse{\equal{\term}{#1}}
{\item \term:   \abbreviation } {}}
}

\begin{itemize}
\itemx{x}
\itemx{Machine Learning}
\itemx{x}
\itemx{Artificial Neural Network}
\itemx{x}
\end{itemize}

\end{document}


Alternative (faster and warning)

Using \DTLassignfirstmatch and changing \PackageError into a \PackageWarning

\documentclass{article}

\usepackage{datatool}

\begin{filecontents*}{abbreviationss2.csv}
"term", "abbreviation"
Machine Learning, ML
Artificial Neural Network, ANN
Artificial Intelligence, AI
\end{filecontents*}

\newcommand{\term}{}
\newcommand{\abbreviation}{}

\makeatletter
\renewcommand*{\dtl@assignfirstmatch}[4]{%
\DTLifdbexists{#2}%
{%
{%
\dtlgetrowindex{\dtl@asg@rowidx}{#2}{\dtlcolumnindex{#2}{#3}}{#1}%
\ifx\dtl@asg@rowidx\dtlnovalue%
\PackageWarning{datatool}{No match found for \string\DTLassignfirstmatch{#2}{#3}{#1}{#4}}{}%
\else
\dtlgetrow{#2}{\dtl@asg@rowidx}%
\@dtl@assign{#4}{#2}%
\fi
}%
}%
{%
\PackageError{datatool}{Data base #2' doesn't exist}{}%
}%
}

\makeatother

\newcommand{\itemx}[1]{%
\renewcommand{\term}{}%
\renewcommand{\abbreviation}{}%
\DTLassignfirstmatch{abbreviations}{term}{#1}{\term=term,\abbreviation=abbreviation}%
\ifthenelse{\equal{\abbreviation}{}}{\item #1:~(\dtlnovalue)}{\item #1:~(\abbreviation)}%
}

\newcommand{\findx}[1]{%
\renewcommand{\term}{}%
\renewcommand{\abbreviation}{}%
\DTLassignfirstmatch{abbreviations}{term}{#1}{\term=term,\abbreviation=abbreviation}%
\ifthenelse{\equal{\abbreviation}{}}{~(\dtlnovalue)}{~(\abbreviation)}%
}

\begin{document}

\DTLdisplaydb{abbreviations}
\bigskip

\begin{itemize}
\itemx{x}
\itemx{Machine Learning}
\itemx{x}
\itemx{Artificial Neural Network}
\itemx{x}
\end{itemize}

\bigskip

Deep Learning\findx{Deep Learning} is a subset of Artificial Neural Network\findx{Artificial Neural Network},
which in turn is a subset of Machine Learning\findx{Machine Learning} and
Artificial Intelligence\findx{Artificial Intelligence}.

\end{document}

• If you don't provide tokens for the "else"-case you can use \DTLforeach*'s optional argument: \DTLforeach*[{\equal{\term}{#1}}]{abbreviations}{\abbreviation=abbreviation, \term=term}{\item#1 : \abbreviation} Aug 9, 2021 at 17:01