TransWikia.com

Create a nested comma-separated-list from a CSV file

TeX - LaTeX Asked on June 2, 2021

In my Latex document I define a nested list that looks like this:

defrecipes{
{
test1@
test2@
test3@
test4@
test5a*test5b@
test6a*test6b
},
{
test1@
test2@
test3@
test4@
test5a*test5b@
test6a*test6b
}%
}

Instead of defining this list in the Latex document, I’d like to parse these data from a CSV file. I looked into various libraries, but so far, I only could find examples that make tables out of a CSV file or I couldn’t understand the example. Isn’t there an easy and obvious way to transform a CSV file like this in the above mentioned list?

test1,test2,test3,test4,test5a*test5b,test6a*test6b
test1,test2,test3,test4,test5a*test5b,test6a*test6b

One Answer

Here's an implementation using expl3. I endeavored to make the resulting macro exactly the same as the one you build manually, although there are several spaces that are probably unwanted.

begin{filecontents*}{jobname.csv}
test1,test2,test3,test4,test5a*test5b,test6a*test6b
test1,test2,test3,test4,test5a*test5b,test6a*test6b
end{filecontents*}

documentclass{article}

ExplSyntaxOn

NewDocumentCommand{makelistfromcsv}{mm}
 {% #1 = control sequence, #2 = file name
  scm_listcsv_make:nn { #1 } { #2 }
 }

ior_new:N g_scm_listcsv_input_ior
clist_new:N l__scm_listcsv_temp_in_clist
clist_new:N l__scm_listcsv_temp_out_clist
tl_new:N l__scm_listcsv_temp_tl

cs_new_protected:Nn scm_listcsv_make:nn
 {
  % clear the "out" clist
  clist_clear:N l__scm_listcsv_temp_out_clist
  % open the input csv file
  ior_open:Nn g_scm_listcsv_input_ior { #2 }
  % read one line at a time
  ior_map_inline:Nn g_scm_listcsv_input_ior
   {
    % store the current line as a clist
    clist_set:Nn l__scm_listcsv_temp_in_clist { ##1 }
    % store the items to the temporary tl adding "@ space"
    tl_set:Nx l__scm_listcsv_temp_tl { {{ ~ clist_use:Nn l__scm_listcsv_temp_in_clist { @~ } ~ }} }
    % add the tokens as an item of the "out" clist
    clist_put_right:NV l__scm_listcsv_temp_out_clist l__scm_listcsv_temp_tl
   }
  % now build the final token list
  tl_new:N #1
  tl_set:Nx #1 { ~ clist_use:Nn l__scm_listcsv_temp_out_clist { , ~ } }
 }

ExplSyntaxOff

defrecipes{
{
test1@
test2@
test3@
test4@
test5a*test5b@
test6a*test6b
},
{
test1@
test2@
test3@
test4@
test5a*test5b@
test6a*test6b
}%
}

makelistfromcsv{newrecipes}{jobname.csv}

ifxrecipesnewrecipes % are they equal?
  typeout{EQUAL :-D}
else
  typeout{UNEQUAL :-(}
fi

The output is

EQUAL :-D

The filecontents* environment is used just for making the example self-contained and you can use any file on your system.

A “spurious-space-free” version:

begin{filecontents*}{jobname.csv}
test1,test2,test3,test4,test5a*test5b,test6a*test6b
test1,test2,test3,test4,test5a*test5b,test6a*test6b
end{filecontents*}

documentclass{article}

ExplSyntaxOn

NewDocumentCommand{makelistfromcsv}{mm}
 {% #1 = control sequence, #2 = file name
  scm_listcsv_make:nn { #1 } { #2 }
 }

ior_new:N g_scm_listcsv_input_ior
clist_new:N l__scm_listcsv_temp_in_clist
clist_new:N l__scm_listcsv_temp_out_clist
tl_new:N l__scm_listcsv_temp_tl

cs_new_protected:Nn scm_listcsv_make:nn
 {
  % clear the "out" clist
  clist_clear:N l__scm_listcsv_temp_out_clist
  % open the input csv file
  ior_open:Nn g_scm_listcsv_input_ior { #2 }
  % read one line at a time
  ior_map_inline:Nn g_scm_listcsv_input_ior
   {
    % store the current line as a clist
    clist_set:Nn l__scm_listcsv_temp_in_clist { ##1 }
    % store the items to the temporary tl adding "@"
    tl_set:Nx l__scm_listcsv_temp_tl { {{clist_use:Nn l__scm_listcsv_temp_in_clist { @ } }} }
    % add the tokens as an item of the "out" clist
    clist_put_right:NV l__scm_listcsv_temp_out_clist l__scm_listcsv_temp_tl
   }
  % now build the final token list
  tl_new:N #1
  tl_set:Nx #1 { clist_use:Nn l__scm_listcsv_temp_out_clist { , } }
 }

ExplSyntaxOff

defrecipes{%
{%
test1@%
test2@%
test3@%
test4@%
test5a*test5b@%
test6a*test6b%
},%
{%
test1@%
test2@%
test3@%
test4@%
test5a*test5b@%
test6a*test6b%
}%
}

makelistfromcsv{newrecipes}{jobname.csv}
ifxrecipesnewrecipes % are they equal?
  typeout{EQUAL :-D}
else
  typeout{UNEQUAL :-(}
fi

With this code, the replacement text of newrecipes is

{test1@test2@test3@test4@test5a*test5b@test6a*test6b},{test1@test2@test3@test4@test5a*test5b@test6a*test6b}

without spaces.

Correct answer by egreg on June 2, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP