1

I use datatool to parse a CSV file. I want to use the makro \DTLminforcolumn to calculate the minimum value of a column. This works fine if every row has a value, but not so much, if there are empty rows. How can I tell datatool to ignore empty rows?

MWE:

\documentclass{article}
\usepackage{filecontents}
\usepackage{datatool}

\begin{filecontents*}{example1.csv}
title;value
test1;1
test2;
test3;3
test4;2
\end{filecontents*}
\begin{filecontents*}{example2.csv}
title;value
test1;1
test2;2
test3;3
test4;4
\end{filecontents*}

\begin{document}
    \DTLsetseparator{;}%
    \DTLloaddb{a}{example1.csv}
    \DTLminforcolumn{a}{value}{\minValueFirstExample}
    
    \DTLsetseparator{;}%
    \DTLloaddb{b}{example2.csv}
    \DTLminforcolumn{b}{value}{\minValueSecondExample}
    
    Min Value1: \minValueFirstExample\\ %Should be 1 and not 2
    Min Value2: \minValueSecondExample %Is correct
\end{document}
1

Add the following lines after loading the datatool package.

\usepackage{xpatch}
\patchcmd\DTLminforcolumn{%
  \DTLmin{#3}{#3}{\DTLthisval}%
}{%
  \ifdefempty\DTLthisval{}{\DTLmin{#3}{#3}{\DTLthisval}}%
}{}{}

enter image description here

\documentclass{article}
\usepackage{datatool}
\usepackage{xpatch}
\patchcmd\DTLminforcolumn{%
  \DTLmin{#3}{#3}{\DTLthisval}%
}{%
  \ifdefempty\DTLthisval{}{\DTLmin{#3}{#3}{\DTLthisval}}%
}{}{}
\begin{document}
    \DTLlistskipemptytrue
    \DTLsetseparator{;}%
    \DTLloaddb{a}{example1.csv}
    \DTLminforcolumn{a}{value}{\minValueFirstExample}
    
    \DTLsetseparator{;}%
    \DTLloaddb{b}{example2.csv}
    \DTLminforcolumn{b}{value}{\minValueSecondExample}

    \noindent
    Min Value1: \minValueFirstExample\\ %Should be 1 and not 2
    Min Value2: \minValueSecondExample %Is correct
\end{document}

Explanation. \DTLminforcolumn is defined in datatool.sty as follows:

\newcommand*{\DTLminforcolumn}[3]{% #1 database, #2 col name, #3 return value
  \def#3{}%                                   #3 := ""
  \DTLifdbexists{#1}{%                        if database #1 exists:
     \@sDTLifhaskey{#1}{#2}{%                   if column #2 exists in db #1:
       \@sdtlforcolumn{\DTLthisval}{#1}{#2}{%     for DTLthisval in column #2:
         \ifdefempty{#3}{%                          if #3 == "":
           \let#3\DTLthisval%                         #3 := DTLthisval
         }{%                                        else:
           \DTLmin{#3}{#3}{\DTLthisval}%              #3 := min(#3,DTLthisval)
         }%                                     
       }% 
     }{%                                        else:
       \PackageError{datatool}{Key `#2' %         raise exception
       doesn't exist in database `#1'}{}%
     }%
  }{%                                         else:
    \PackageError{datatool}{Data base %         raise exception
    `#1' doesn't exist}{}%
  }%
}

\DTLmin returns 'empty' if any of the two numbers is empty. So, when \DTLthisval happens to be empty (because the field in the column is empty), the minimum computed so far in #3 will be reset to 'empty', as it was before the loop started.

To change this behavior we have to make sure that the minimum is only computed for non-empty fields. Therefore we use \patchcmd from the package xpatch to replace in \DTLminforcolumn the line

\DTLmin{#3}{#3}{\DTLthisval}%     #3 := min(#3,DTLthisval)

by

\ifdefempty\DTLthisval{%          if DTLthisval == "":
}{%                               else:
  \DTLmin{#3}{#3}{\DTLthisval}%     #3 := min(#3,DTLthisval)
}%
3
  • Thanks for the help. Can you explain me what this does? Btw, I found out, that \DTLminforkeys also works correctly. – SCM Jan 19 at 20:30
  • @SCM I have added an explanation. – gernot Jan 19 at 21:27
  • Thanks a lot @gernot – SCM Jan 21 at 23:33

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.