I've been very impressed with xparse and expl3, and have written a wonderful document command using \ProcessList that takes a comma separated list and turns it into a (basically) 1 column table.

-> \begin{tabular}{rr}
   \tiny 1st & A \\
   \tiny 2nd & B \\
   \tiny 3rd & C \\
   \tiny 4th & D

Now I've become mad with the power and want to specify more than one column at a time. Something like:

\stacksy{ 5: A,B,C,D ; 6: A,C,D,B ; 3: C,A,B,D }
-> \begin{tabular}{rrrr}
             & 5 & 6 & 3 \\ \hline
   \tiny 1st & A & A & C \\
   \tiny 2nd & B & C & A \\
   \tiny 3rd & C & D & B \\
   \tiny 4th & D & B & D \\

Everything is fine, except that the input is in column-major order, but TeX (or the tabular environment) expects row-major order.

How do I transpose the input list of lists?

  • Not an answer, but it's important to note that xparse is about 'parsing' in the sense 'reading LaTeX2e command syntax' rather than 'parsing arbitrary input'.
    – Joseph Wright
    Sep 5, 2012 at 16:13
  • @JosephWright: Does it sound right that I should be using the sequence splits and maps from expl3 instead of ProcessList from xparse? Scott's solution is working very well, and I'll likely rewrite my single-stack version to use those ideas. Sep 5, 2012 at 18:49
  • No, as what you are doing is defining an input syntax. I'm simply flagging up that \ProcessList and the like can only take you so far: we've been asked before about extremely complex input, which is really beyond the scope of xparse. Currently, so are nested lists (indeed, I'd have to think hard about a suitable syntax!).
    – Joseph Wright
    Sep 5, 2012 at 19:06

1 Answer 1


Here is a method that will produce a rectangular array of numbers that are given as columns.

  • The syntax is stacksys{col 1;col 2;...;col n} where col i is the ith column with entries given as a comma separated list.
  • I fiddled with the input a bit to show how column and row headings can be introduced.
  • array can be changed to tabular if that is desired.
  • \stacksys{,r1,r2;c1,1,2;c2,3,4;c3,5,6} produces:

enter image description here


\int_new:N \l_ss_num_cols_int
\int_new:N \l_ss_num_rows_int
\bool_new:N \l_has_run_bool

\NewDocumentCommand{\stacksys} {m}
        \ss_make_table:n {#1}
        \int_step_inline:nnnn {1}{1}{\l_ss_num_rows_int}
                \tl_put_right:cn {l_row_{##1}_tl}{\\}
        \begin{array}{*{\int_use:N \l_ss_num_cols_int}{c}}
        \int_step_inline:nnnn {1}{1}{\l_ss_num_rows_int}
                \tl_use:c {l_row_{##1}_tl}
        \bool_gset_false:N \l_has_run_bool

\cs_new_protected:Npn \ss_make_table:n #1
        \seq_set_split:Nnn \l_tmpa_seq {;} {#1}
        \int_set:Nn \l_ss_num_cols_int {\seq_count:N \l_tmpa_seq}
        \seq_map_function:NN \l_tmpa_seq \ss_process_cols:n

\cs_new_protected:Npn \ss_process_cols:n #1
        \int_zero:N \l_tmpa_int
        \seq_set_split:Nnn \l_tmpb_seq {,} {#1}
        \int_set:Nn \l_ss_num_rows_int {\seq_count:N \l_tmpb_seq}
        \seq_map_inline:Nn \l_tmpb_seq
                \bool_if:NTF \l_has_run_bool
                      \int_incr:N \l_tmpa_int
                      \tl_put_right:cn {l_row_{\int_use:N \l_tmpa_int}_tl} {&##1}
                      \int_incr:N \l_tmpa_int
                      \tl_clear_new:c {l_row_{\int_use:N \l_tmpa_int}_tl}
                      \tl_put_right:cn {l_row_{\int_use:N \l_tmpa_int}_tl} {##1}
        \bool_gset_true:N \l_has_run_bool

  • Thanks! I'm adjusting it now to my specific situation (and taking care of 100 other tasks). Once I've got it working, I'll accept. Your use of :c is so simple and clear, thanks! [current project is detecting the last row, since somehow I'm getting an extra visible row when using {r|rrr} style tabular headings] Sep 5, 2012 at 18:45
  • Officially, the colon is supposed to work in \seq_set_split:Nnn right? I agree it doesn't, and I couldn't make it work. The hyphen looks fine, so this is only for curiosity. Sep 5, 2012 at 18:46
  • I'm not exactly sure, I thought that it "should" work (I couldn't find it excluded in the documentation), but was getting errors so I changed it to the "-". You can switch that to something else if you like.
    – Scott H.
    Sep 5, 2012 at 18:49
  • @ScottH. A few 'LaTeX3 Best Practice' comments. I'd encourage you to pick a single prefix here, both for functions and variables (namespace management is important!): perhaps tabtran? Most of your stuff is not expandable, so \cs_new_protected:Npn would be better than \cs_new:Npn. In 'integer expression' contexts you don't need \int_use:N as TeX automatically does this, so we tend to leave them out.
    – Joseph Wright
    Sep 5, 2012 at 19:10
  • @JosephWright Thanks for the suggestions. I've been trying to remember to add a unique prefix, but forgot this time! For the second I'll have to read up on protected as I don't really understand when it is required. I interpret the third as meaning that if an integer expression is expected, then the \int_use:N is unnecessary because \int_eval is applied anyway?
    – Scott H.
    Sep 5, 2012 at 19:21

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.