4

I would like to create a macro that can generate a table like this:

\documentclass{minimal}

\begin{document}

\begin{tabular}{c|c|c}
  A & 1 & 2 & 3 \\
  B & 0 & 1 & 1 \\
  C & 0 & 0 & 1
\end{tabular}

\end{document}

I would like to be able to define a list \columns{1,2,3} which contains the headers of each column, a list \rows{A,B,C} which contains the first element in each row.

For the rest of the entries, maybe something like \entries{011,001}, I'm not sure what the best way to do this would be.

Ultimately, I'd like a macro with a syntax like

\mymacro{\columns}{\rows}{\entries}

I've only recently started writing my own macros, and I haven't been able to find anything that does what I want and am not sure how to proceed.

Thanks!

1 Answer 1

3

Interesting question, but is there a real gain?

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
% user interface
\NewDocumentCommand{\mymacro}{mmm}
 {% #1 = column headers, #2 = row headers, #3 = entries
  \gabriel_mymacro:nnn { #1 } { #2 } { #3 }
 }

% variables
\tl_new:N \l__gabriel_mymacro_body_tl
\tl_new:N \l__gabriel_mymacro_row_tl
\seq_new:N \l__gabriel_mymacro_entries_seq

% functions
\cs_new_protected:Nn \gabriel_mymacro:nnn
 {
  % clear the tl containing the body and collect the entries
  \tl_clear:N \l__gabriel_mymacro_body_tl
  \seq_set_split:Nnn \l__gabriel_mymacro_entries_seq { , } { #3 }
  % make the first row
  \tl_put_right:Nx \l__gabriel_mymacro_body_tl
   {
    \clist_item:nn { #2 } { 1 }
   }
  \clist_map_function:nN { #1 } \__gabriel_mymacro_add_entry:n
  \tl_put_right:Nn \l__gabriel_mymacro_body_tl { \\ \hline }
  % build the following rows
  \int_step_inline:nn { \clist_count:n { #2 } - 1 }
   {
    \tl_put_right:Nx \l__gabriel_mymacro_body_tl
     {
      \clist_item:nn { #2 } { ##1 + 1 }
     }
    \seq_pop_left:NN \l__gabriel_mymacro_entries_seq \l__gabriel_mymacro_row_tl
    \tl_map_function:NN \l__gabriel_mymacro_row_tl \__gabriel_mymacro_add_entry:n
    \tl_put_right:Nn \l__gabriel_mymacro_body_tl { \\ }
   }
  % typeset the table
  \begin{tabular}{c *{ \clist_count:n { #1 } } { |c } }
  \tl_use:N \l__gabriel_mymacro_body_tl
  \end{tabular}
 }

\cs_new_protected:Nn \__gabriel_mymacro_add_entry:n
 {
  \tl_put_right:Nn \l__gabriel_mymacro_body_tl { & #1 }
 }

\ExplSyntaxOff

\begin{document}

\mymacro{1,2,3}{A,B,C}{011,001}

\bigskip

\mymacro{Apple,Banana,Cherry,Date}{X,Apple,Banana,Cherry,Date}{
 0000,0101,1111,1010
}

\end{document}

enter image description here

A variant with an optional argument to specify the column alignments.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
% user interface
\NewDocumentCommand{\mymacro}{ommm}
 {% #2 = column headers, #3 = row headers, #4 = entries
  \IfNoValueTF { #1 }
   {
    \gabriel_mymacro:nnnn { c *{ \clist_count:n { #2 } } { |c } } { #2 } { #3 } { #4 }
   }
   {
    \gabriel_mymacro:nnnn { #1 } { #2 } { #3 } { #4 }
   }
 }

% variables
\tl_new:N \l__gabriel_mymacro_body_tl
\tl_new:N \l__gabriel_mymacro_row_tl
\seq_new:N \l__gabriel_mymacro_entries_seq

% functions
\cs_new_protected:Nn \gabriel_mymacro:nnnn
 {
  % clear the tl containing the body and collect the entries
  \tl_clear:N \l__gabriel_mymacro_body_tl
  \seq_set_split:Nnn \l__gabriel_mymacro_entries_seq { , } { #4 }
  % make the first row
  \tl_put_right:Nx \l__gabriel_mymacro_body_tl
   {
    \clist_item:nn { #3 } { 1 }
   }
  \clist_map_function:nN { #2 } \__gabriel_mymacro_add_entry:n
  \tl_put_right:Nn \l__gabriel_mymacro_body_tl { \\ \hline }
  % build the following rows
  \int_step_inline:nn { \clist_count:n { #3 } - 1 }
   {
    \tl_put_right:Nx \l__gabriel_mymacro_body_tl
     {
      \clist_item:nn { #3 } { ##1 + 1 }
     }
    \seq_pop_left:NN \l__gabriel_mymacro_entries_seq \l__gabriel_mymacro_row_tl
    \tl_map_function:NN \l__gabriel_mymacro_row_tl \__gabriel_mymacro_add_entry:n
    \tl_put_right:Nn \l__gabriel_mymacro_body_tl { \\ }
   }
  % typeset the table
  \begin{tabular}{#1}
  \tl_use:N \l__gabriel_mymacro_body_tl
  \end{tabular}
 }

\cs_new_protected:Nn \__gabriel_mymacro_add_entry:n
 {
  \tl_put_right:Nn \l__gabriel_mymacro_body_tl { & #1 }
 }

\ExplSyntaxOff

\begin{document}

\mymacro{1,2,3}{A,B,C}{011,001}

\bigskip

\mymacro{Apple,Banana,Cherry,Date}{X,Apple,Banana,Cherry,Date}{
 0000,0101,1111,1010
}

\bigskip

\mymacro[l|cccc]{Apple,Banana,Cherry,Date}{X,Apple,Banana,Cherry,Date}{
 0000,0101,1111,1010
}

\end{document}

enter image description here

5
  • I get the following error when I try to compile your code ! Undefined control sequence. \gabriel_mymacro:nnn ...line }\int_step_inline:nn {\clist_count:n {#2}-1}{\t...
    – user17249
    Commented Nov 5, 2018 at 23:32
  • @Gabriel Update your TeX system and retry.
    – egreg
    Commented Nov 5, 2018 at 23:33
  • Updated and it works now, thank you! As for the real gain from this, I plan on extending it slightly for a kind of table that I end up having to make really frequently, so this will definitely be a huge time saver
    – user17249
    Commented Nov 5, 2018 at 23:55
  • How would I extend this to have a fourth argument \mymacro{1,2,3}{a,b,c}{001,011}{c|cc} where I specify the column separators?
    – user17249
    Commented Nov 6, 2018 at 0:00
  • @Gabriel Variant version added.
    – egreg
    Commented Nov 6, 2018 at 0:11

You must log in to answer this question.

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