Following up this answer and the ensuing discussion, \getRow is expected to check the existence and emptiness of the target row of a CSV data, then save that row columns to another macro.
In other words, the following code snippet of \getRow doesn't process any data of the target row after a successful check for the emptiness and existence of that row.
% star → global assignment, macro, key, label
\NewDocumentCommand \getRow { s m m m }
{
\IfBooleanTF {#1}
{ \__diaa_get_row:NNnn \cs_gset_protected:Npx }
{ \__diaa_get_row:NNnn \cs_set_protected:Npx }
#2 {#3} {#4}
}
\cs_new_protected:Npn \__diaa_get_row:NNnn #1 #2 #3 #4
{
#1 #2 ##1 [ ##2 ]
{ \exp_not:N \msg_expandable_error:nnn { diaa } { improper-row } { \cs_to_str:N #2 } }
\__diaa_csv_item_exist:nnNT {#4} {#3} \l__diaa_tmpa_tl
{
\bool_set_true:N \l__diaa_empty_item_bool
\tl_map_inline:Nn \l__diaa_tmpa_tl
{
\tl_if_empty:nF {##1}
{ \bool_set_false:N \l__diaa_empty_item_bool }
}
\bool_if:NTF \l__diaa_empty_item_bool
{ \msg_warning:nnnn { diaa } { row-empty } {#3} {#4} }
#1 #2 ##1 [ ##2 ]
{
\exp_not:N \__diaa_get_column:nnN
{ \exp_not:V \l__diaa_tmpa_tl } {##2} ##1
}
}
}
\cs_new_protected:Npn __diaa_get_column:nnN #1 #2 #3
{
\str_if_eq:nnTF {#2} { non-empty }
{
__diaa_nb_nonempty_items_in_row:nn { 0 } #1
\q_recursion_tail \q_recursion_stop
}
{ __diaa_check_column_range:nn {#1} {#2} }
}
\cs_new:Npn __diaa_nb_nonempty_items_in_row:nn #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2} { \int_eval:n {#1} }
\tl_if_empty:nTF {#2}
{ __diaa_nb_nonempty_items_in_row:nn {#1} }
{ __diaa_nb_nonempty_items_in_row:nn { #1 + 1 } }
}
\msg_new:nnn { diaa } { csv-undefined } { CSV~database~#1'~undefined! } \msg_new:nnn { diaa } { key-undefined } { CSV~#2'~has~no~key~#1'! } \msg_new:nnn { diaa } { out-of-range } { Index~#1~out~of~range! } \msg_new:nnn { diaa } { item-empty } { Item~#1~from~#2'~in~CSV~#3'~is~empty! } \msg_new:nnn { diaa } { row-empty } { Row~#1'~in~CSV~`#2'~is~empty! }
\msg_new:nnn { diaa } { empty-row-item }
{ Empty~item~#1~\msg_line_context:! }
\msg_new:nnn { diaa } { improper-row }
{
Improper~row~macro~\iow_char:N \#1~\msg_line_context:.\
The~\iow_char:N \getRow~command~did~not~succeed.
}
So, how to make \getRow process the row data after passing the checks for emptiness and existence for the following full MWE?
P.S. I included the full expl3 code since I don't know if there are dependencies somewhere or not
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , {foo, bar} ,
Second Parameter , 3 , 6 , 44
Empty Parameter , , ,
\end{filecontents*}
\documentclass{article}
\usepackage{xparse,siunitx,xfp}
\ExplSyntaxOn
% Step 1: reading the file
\ior_new:N \l__diaa_csv_ior
\bool_new:N \l__diaa_csv_str_bool
\bool_new:N \l__diaa_empty_item_bool
\seq_new:N \l__diaa_csv_tmp_seq
\tl_new:N \l__diaa_tmpa_tl
% str mode (bool/star), key column, label, value columns, file
\NewDocumentCommand \ReadCSV { s O{1} m O{} m }
{
\IfBooleanTF {#1}
{ \bool_set_true:N \l__diaa_csv_str_bool }
{ \bool_set_false:N \l__diaa_csv_str_bool }
\diaa_csv_read:nnnn {#3} {#2} {#4} {#5}
}
% label, key column, value columns, file
\cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
{
\tl_if_blank:nTF {#3} % Detect number of columns and use 2 to last
{
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_get:NN }
{ \ior_get:NN }
\l__diaa_csv_ior \l_tmpa_tl
\ior_close:N \l__diaa_csv_ior
\seq_set_split:NnV \l_tmpa_seq { , } \l_tmpa_tl
\seq_clear:N \l__diaa_csv_tmp_seq
\int_step_inline:nnn { 2 } { \seq_count:N \l_tmpa_seq }
{ \seq_put_right:Nn \l__diaa_csv_tmp_seq {##1} }
}
{ \msg_expandable_error:nnn { diaa } { file-not-found } {#4} }
}
{ \seq_set_split:Nnn \l__diaa_csv_tmp_seq { , } {#3} } % explicit columns
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\prop_new:c { g__diaa_csv_#1_prop }
__diaa_csv_read:nn {#1} {#2}
\ior_close:N \l__diaa_csv_ior
}
{ \msg_expandable_error:nnn { diaa } { file-not-found } {#4} }
}
\msg_new:nnn { diaa } { file-not-found }
{ File~`#1'~not~found. }
\cs_generate_variant:Nn \prop_put:Nnn { cxV }
% label, key column
\cs_new_protected:Npn __diaa_csv_read:nn #1 #2
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_map_inline:Nn }
{ \ior_map_inline:Nn }
\l__diaa_csv_ior
{
\seq_set_split:Nnn \l_tmpa_seq { , } {##1} % split one CSV row
\tl_clear:N \l_tmpa_tl
\seq_map_inline:Nn \l__diaa_csv_tmp_seq
{ \tl_put_right:Nx \l_tmpa_tl { { \seq_item:Nn \l_tmpa_seq {####1} } } }
\prop_put:cxV { g__diaa_csv_#1_prop }
{ \seq_item:Nn \l_tmpa_seq {#2} }
\l_tmpa_tl
}
}
% Step 2: getting the values
% star → global assignment, macro or tl var, value column, key, label
\NewDocumentCommand \getValue { s m O{1} m m }
{
\tl_clear:N \l__diaa_tmpa_tl
\diaa_csv_item:nnnN {#4} {#3} {#5} \l__diaa_tmpa_tl
\IfBooleanTF {#1}
{ \tl_gset_eq:NN } { \tl_set_eq:NN }
#2 \l__diaa_tmpa_tl
\tl_if_empty:NT #2
{ \msg_warning:nnnnn { diaa } { item-empty } {#3} {#4} {#5} }
}
% key, value column, label
\cs_new_protected:Npn \diaa_csv_item:nnnN #1 #2 #3 #4
{
__diaa_csv_item_exist:nnNT {#3} {#1} #4
{ \exp_args:NV __diaa_check_column_range:nn #4 {#2} }
}
\cs_new_protected:Npn __diaa_check_column_range:nn #1 #2
{
\bool_lazy_or:nnTF
{ \int_compare_p:nNn {#2} = { 0 } }
{ \int_compare_p:nNn { \tl_count:n {#1} } < { \int_abs:n {#2} } }
{ \msg_expandable_error:nnn { diaa } { out-of-range } {#2} }
{ \tl_set:Nx \l__diaa_tmpa_tl { \tl_item:nn {#1} {#2} } }
}
\prg_new_protected_conditional:Npnn __diaa_csv_item_exist:nnN #1 #2 #3 { T }
{
\prop_if_exist:cTF { g__diaa_csv_#1_prop }
{
\prop_get:cnNTF { g__diaa_csv_#1_prop } {#2} #3
{ \prg_return_true: }
{
\msg_expandable_error:nnnn { diaa } { key-undefined } {#2} {#1}
\prg_return_false:
}
}
{
\msg_expandable_error:nnn { diaa } { csv-undefined } {#1}
\prg_return_false:
}
}
\cs_new_protected:Npn __diaa_check_empty:nn #1 #2
{
\tl_if_empty:nT {#1}
{ \msg_warning:nnn { diaa } { empty-row-item } {#2} }
#1
}
% star → global assignment, macro, key, label
\NewDocumentCommand \getRow { s m m m }
{
\IfBooleanTF {#1}
{ __diaa_get_row:NNnn \cs_gset_protected:Npx }
{ __diaa_get_row:NNnn \cs_set_protected:Npx }
#2 {#3} {#4}
}
\cs_new_protected:Npn __diaa_get_row:NNnn #1 #2 #3 #4
{
#1 #2 ##1 [ ##2 ]
{ \exp_not:N \msg_expandable_error:nnn { diaa } { improper-row } { \cs_to_str:N #2 } }
__diaa_csv_item_exist:nnNT {#4} {#3} \l__diaa_tmpa_tl
{
\bool_set_true:N \l__diaa_empty_item_bool
\tl_map_inline:Nn \l__diaa_tmpa_tl
{
\tl_if_empty:nF {##1}
{ \bool_set_false:N \l__diaa_empty_item_bool }
}
\bool_if:NTF \l__diaa_empty_item_bool
{ \msg_warning:nnnn { diaa } { row-empty } {#3} {#4} }
#1 #2 ##1 [ ##2 ]
{
\exp_not:N __diaa_get_column:nnN
{ \exp_not:V \l__diaa_tmpa_tl } {##2} ##1
}
}
}
\cs_new_protected:Npn __diaa_get_column:nnN #1 #2 #3
{
\str_if_eq:nnTF {#2} { non-empty }
{
__diaa_nb_nonempty_items_in_row:nn { 0 } #1
\q_recursion_tail \q_recursion_stop
}
{ __diaa_check_column_range:nn {#1} {#2} }
}
\cs_new:Npn __diaa_nb_nonempty_items_in_row:nn #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2} { \int_eval:n {#1} }
\tl_if_empty:nTF {#2}
{ __diaa_nb_nonempty_items_in_row:nn {#1} }
{ __diaa_nb_nonempty_items_in_row:nn { #1 + 1 } }
}
\msg_new:nnn { diaa } { csv-undefined } { CSV~database~#1'~undefined! } \msg_new:nnn { diaa } { key-undefined } { CSV~#2'~has~no~key~#1'! } \msg_new:nnn { diaa } { out-of-range } { Index~#1~out~of~range! } \msg_new:nnn { diaa } { item-empty } { Item~#1~from~#2'~in~CSV~#3'~is~empty! } \msg_new:nnn { diaa } { row-empty } { Row~#1'~in~CSV~`#2'~is~empty! }
\msg_new:nnn { diaa } { empty-row-item }
{ Empty~item~#1~\msg_line_context:! }
\msg_new:nnn { diaa } { improper-row }
{
Improper~row~macro~\iow_char:N \#1~\msg_line_context:.\
The~\iow_char:N \getRow~command~did~not~succeed.
}
\ExplSyntaxOff
\parindent0pt
\begin{document}
\ReadCSV{mydata}{test.csv}
\getRow\myRow{Second Parameter}{mydata}
\num{\myRow[2]}\\
\num{\myRow[1]}\\
\edef\Value{\fpeval{\myRow[2]-\myRow[1]}}
\num[\Value]
\end{document}

\getRowworks in order to satisfy your requirements regarding error messages. I don't have the time to read these changes, but it is very clear that you are using the old syntax in your example here. The syntax in the answer you linked to requires three steps every time: 1) Using\getRow\somemacro{<row ID>}{<CSV ID>}. 2) Calling\somemacro\othermacro[<index>]. 3) Using\othermacro(step 1 assigns to\somemacro, step 2 to\othermacro). – frougon Dec 26 '20 at 20:28\getRow\myRow{Second Parameter}{mydata}followed by\myRow[2]does not conform to\getRow's usage as explained at the beginning of Phelype's answer, which your question refers to. See how\<row macro>is used in Phelype's answer. – frougon Dec 26 '20 at 20:39\getRow\myValue{Second Parameter}{mydata} \myValue\myValuee[2]? I followed the same syntax and no output. – Diaa Dec 26 '20 at 20:49\myValuee. This is the macro containing the result. Reread my 3 steps above... – frougon Dec 26 '20 at 20:50Undefined control sequence.l.202 \myValuefor the following:\begin{document} \ReadCSV{mydata}{test.csv} \getRow\myRow{Second Parameter}{mydata} \myRow\myValue[2]; \myValue \end{document}– Diaa Dec 26 '20 at 20:57\__diaa_get_column:nnNnever assigns to its third argument. If you insert\tl_set_eq:NN #3 \l__diaa_tmpa_tlafter\__diaa_check_column_range:nn {#1} {#2}in the definition of\__diaa_get_column:nnN, it works for this example. – frougon Dec 26 '20 at 21:12non-emptycase would also need a similar fix, or else to be used in a clunky way:\getRow\myRow{Second Parameter}{mydata}followed by\myRow\unused[non-empty]where\unusedis, well, unused). – frougon Dec 26 '20 at 21:17\getRowwork as in your old code so that I can keep the current error/warning messages of this answer, but also use it in a straightforward way like\num{\myRow[2]}in stead of the extra steps in addition to losing the compactness of using the values in the list without having to extract every single one? – Diaa Dec 26 '20 at 21:23\cs_new_protected:Npn \__diaa_get_column:nnN #1 #2 #3 { \str_if_eq:nnTF {#2} { non-empty } { \tl_set:Nx #3 { \__diaa_nb_nonempty_items_in_row:nn { 0 } #1 \q_recursion_tail \q_recursion_stop } } { \__diaa_check_column_range:nn {#1} {#2} \tl_set_eq:NN #3 \l__diaa_tmpa_tl } }. To reply to your last question: sorry, but Phelype changed the API to do something you asked regarding the error messages. – frougon Dec 26 '20 at 21:25\num{...}, but not intermediate results (from steps 1 and 2). – frougon Dec 26 '20 at 21:26:)– Diaa Dec 26 '20 at 21:31