1

The following code is supposed to get values from the first row of mydata.csv and then copy values to a new empty row.

I am using foreachitem to pass the key to the DTLgetvalue function. It compiles, but it doesn't take each key from the itemlist, instead it seems to use the last key of the item list. Am I missing something about how to pass a listofitems to the DTLfuction or does the \tmp variable not refresh?

\documentclass[a4paper,10pt]{article}

\usepackage{datatool}
\usepackage{listofitems}
\usepackage{tabularx}


\begin{filecontents*}{mydata.csv}
ColA,   ColB,     ColC
John,   Doe,      1
Eve,    Johansen, 2
Scott,  Great,    3
\end{filecontents*}

\DTLloaddb[keys={ColA,ColB,ColC}]{mydata}{mydata.csv}

\readlist\keylist{ColA,ColB,ColC} %%% making a list of header-keys

\newcommand{\addemptyrow}{%
    \DTLnewrow{mydata}%%%
    \foreachitem\key\in\keylist{%
        \DTLgetvalue{\tmp}{mydata}{1}{\dtlcolumnindex{mydata}{\key}}%
        \DTLnewdbentry{mydata}{\key}{\tmp}}
        }

\begin{document}
\DTLdisplaydb{mydata}

\addemptyrow{}

\DTLdisplaydb{mydata}

\end{document}

2 Answers 2

1

So, I solved it. It's an issue of using expand. Still haven't fully grasped the idea of expand but found a similar old thread here

So, the above works if I add \dtlexpandnewvalue in the macro

\readlist\keylist{ColA,ColB,ColC}
\newcommand{\addemptyrow}{%
    \DTLnewrow{mydata}%%%
    \dtlexpandnewvalue
    \foreachitem\key\in\keylist{%
        \DTLgetvalue{\tmp}{mydata}{1}{\dtlcolumnindex{mydata}{\key}}%
        \DTLnewdbentry{mydata}{\key}{\tmp}}
        \dtlnoexpandnewvalue
        }

Without the \dtlexpandnewvalue i just saw the last value of \tmp (ie.e "1") not workinghere using \dtlexpandnewvalue solves the issue enter image description here

1
  • 1
    I'd add \dtlnoexpandnewvalue outside of the \foreachitem loop.
    – egreg
    Apr 7, 2020 at 15:20
0

The problem is that \tmp doesn't get expanded, so you're actually adding \tmp in each cell.

There are various ways to solve the issue. Here I present a different one based on xparse and expl3 that also makes easier to manage loops over a list.

\begin{filecontents*}{\jobname.csv}
ColA,   ColB,     ColC
John,   Doe,      1
Eve,    Johansen, 2
Scott,  Great,    3
\end{filecontents*}

\documentclass[a4paper,10pt]{article}

\usepackage{datatool}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\makelist}{mm}
 {
  \clist_clear_new:c { l__sorenj_list_#1_clist }
  \clist_set:cn { l__sorenj_list_#1_clist } { #2 }
 }
\NewDocumentCommand{\maplist}{mm}
 {
  \cs_set_protected:Nn \__sorenj_list_map:n { #2 }
  \clist_map_function:cN { l__sorenj_list_#1_clist } \__sorenj_list_map:n
 }
\NewDocumentCommand{\xDTLnewdbentry}{mmm}
 {
  \exp_args:Nnno \DTLnewdbentry { #1 } { #2 } { #3 }
 }
\ExplSyntaxOff

\DTLloaddb[keys={ColA,ColB,ColC}]{mydata}{\jobname.csv}
\makelist{keylist}{ColA,ColB,ColC} %%% making a list of header-keys

\begin{document}

\DTLdisplaydb{mydata}

\DTLnewrow{mydata}

\maplist{keylist}{%
  \DTLgetvalue{\tmp}{mydata}{1}{\dtlcolumnindex{mydata}{#1}}%
  \xDTLnewdbentry{mydata}{#1}{\tmp}%
}

\DTLdisplaydb{mydata}

\end{document}

There is no \key needed, you just denote the current item with #1. Note it should become ##1 if you embed the loop inside the definition of another macro.

Expansion of \tmp is the only other thing to care about, which is the job of \exp_args:NnnV which jumps over the first token (in this case \DTLnewdbentry) and the following two braced groups, to expand the value of the next token (which is \tmp).

enter image description here

1
  • Cheers! - I am not familiar with xpl3, but I recon your solution also solves the problem.
    – SorenJ
    Apr 7, 2020 at 22:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .