2

In the code from the topic I want to create a new command for making a table, parameters in the command would fill up the table

The separator is a comma in:

\cards{3,1,6,1,2,2,3,2,4,3,4,3,5,7,5,4}

But in my data, there are many entries containing a comma: for example "red, blue Tshirt"

How can I change the separator to pipe | so that I can type the following?

\cards{3|1|6|1|2|2|3|2|4|3|4|3|5|7|5|4}

Minimal code:

\documentclass{article}
\usepackage{booktabs}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\cards}{ m }
 {
  \arne_card_distribution:n { #1 }
 }

\cs_new_protected:Nn \arne_card_distribution:n
 {
  \begin{tabular}{ *{5}{c} }
  \toprule
  & lente & zomer & herfst & winter \\
  \cmidrule{2-5}
  B & \clist_item:nn { #1 } { 1 }
    & \clist_item:nn { #1 } { 2 }
    & \clist_item:nn { #1 } { 3 }
    & \clist_item:nn { #1 } { 4 } \\
  G & \clist_item:nn { #1 } { 5 }
    & \clist_item:nn { #1 } { 6 }
    & \clist_item:nn { #1 } { 7 }
    & \clist_item:nn { #1 } { 8 } \\
  D & \clist_item:nn { #1 } { 9 }
    & \clist_item:nn { #1 } { 10 }
    & \clist_item:nn { #1 } { 11 }
    & \clist_item:nn { #1 } { 12 } \\
  S & \clist_item:nn { #1 } { 13 }
    & \clist_item:nn { #1 } { 14 }
    & \clist_item:nn { #1 } { 15 }
    & \clist_item:nn { #1 } { 16 } \\
  \bottomrule
  \end{tabular}
 }
\ExplSyntaxOff

\begin{document}

\cards{3,1,6,1,2,2,3,2,4,3,4,3,5,7,5,4}


\end{document}
latexforti
  • 2,091
  • 1
    Will your table always have 4 rows and 4 columns, or it may increase depending on the number of items in the argument? – Phelype Oleinik Sep 23 '19 at 23:53
  • @PhelypeOleinik thanks.can change number of items. I want to change seperator between data in \cards.. – latexforti Sep 23 '19 at 23:56
  • 2
    Yes, that's quite easy to do, I'll post an answer. I want to know what do you want to happen if you pass, say, 12 items to \cards, or 20 items, instead of 16. Should the table change in size or should there be an error? – Phelype Oleinik Sep 24 '19 at 00:01
  • You are right. The table size can 12,16,20,24,28.. – latexforti Sep 24 '19 at 00:13

1 Answers1

5

Here's a slightly (over?)worked version of the table. I made it a bit more flexible regarding the number of rows and columns you can use.

The \cards command now takes an optional argument plus three mandatory arguments:

\cards[<key-val>]{<header row>}{<first column>}{<table body>}

The optional argument lets you set the separator using sep=<sep> (default is sep=|) and the alignment of the table cells using align=<token> (default is align=c). The <header row> is a list of <sep>-separated items which will be typeset at the top of the table, preceded by an empty cell. The <first column> is also a list of <sep>-separated items which will be typeset at each row of the table, starting from the second row.

The <table body> is a <sep>-separated list of M×N items (M and N are the number of items in the <header row> and <first column> lists) which will be added one by one to the table in row-major order (that is, the first M items are put in the first row, the next M items are put in the second row, and so on.

The table from your question can be typeset with (newlines added for clarity; spaces are trimmed around the items):

\cards{ lente | zomer | herfst | winter }
      { B | G | D | S }
      {3|1|6|1|
       2|2|3|2|
       4|3|4|3|
       5|7|5|4}

An example using the options:

\cards[sep = /, align = l]
  { foo / bar / baz }
  { F   / B   / B }
  {1/2/3/
   4/5/6/
   7/8/9}

Output:

enter image description here

Code:

\documentclass{article}
\usepackage{booktabs}
\usepackage{xparse}

\ExplSyntaxOn
\tl_new:N \l__forti_table_sep_tl
\tl_new:N \l__forti_table_align_tl
\tl_new:N \l__forti_table_temp_tl
\int_new:N \l__forti_table_ncols_int
\seq_new:N \g__forti_table_body_seq
\seq_new:N \g__forti_table_header_seq
\seq_new:N \g__forti_table_column_seq
\cs_generate_variant:Nn \seq_gset_split:Nnn { Nx }
\keys_define:nn { forti }
  {
    , sep .tl_set:N = \l__forti_table_sep_tl
    , sep .initial:n = { | }
    , sep .value_required:n = true
    , align .tl_set:N = \l__forti_table_align_tl
    , align .initial:n = { c }
    , align .value_required:n = true
  }
\NewDocumentCommand { \cards } { o m m m }
  {
    \group_begin:
      \IfValueT {#1} { \keys_set:nn { forti } {#1} }
      \forti_card_distribution:nnn {#2} {#3} {#4}
    \group_end:
  }
\cs_new_protected:Npn \forti_card_distribution:nnn #1 #2 #3
  {
    \__forti_seq_gset_split:Nn \g__forti_table_header_seq {#1}
    \__forti_seq_gset_split:Nn \g__forti_table_column_seq {#2}
    \__forti_seq_gset_split:Nn \g__forti_table_body_seq   {#3}
    \__forti_card_distribution_check_do:nnnN
      { \seq_count:N \g__forti_table_header_seq }
      { \seq_count:N \g__forti_table_column_seq }
      { \seq_count:N \g__forti_table_body_seq }
    \__forti_card_distribution:nn
  }
\cs_new_protected:Npn \__forti_seq_gset_split:Nn #1 #2
  { \seq_gset_split:Nxn #1 { \tl_use:N \l__forti_table_sep_tl } {#2} }
\cs_new_protected:Npn \__forti_card_distribution_check_do:nnnN #1 #2 #3 #4
  {
    \int_compare:nNnTF {#1*#2} = {#3}
      { #4 {#1} {#2} }
      {
        \msg_error:nnxx { forti } { wrong-number-of-items }
          { \int_eval:n {#1*#2} } {#3}
      }
  }
\cs_new_protected:Npn \__forti_card_distribution:nn #1 #2
  {
    \int_set:Nn \l__forti_table_ncols_int {#1}
    \use:x
      {
        \exp_not:N \begin{tabular}
          { * { \int_eval:n {#1 + 1} } { \tl_use:N \l__forti_table_align_tl } }
      }
      \toprule
        \c_alignment_token
        \seq_use:Nn \g__forti_table_header_seq { \c_alignment_token } \\
      \cmidrule { 2 - \int_eval:n {#1 + 1} }
        \int_step_function:nN { #2 }
          \__forti_typeset_table_row:n
      \bottomrule
    \end{tabular}
  }
\cs_new_protected:Npn \__forti_typeset_table_row:n #1
  {
    \seq_gpop:NN \g__forti_table_column_seq \l__forti_table_temp_tl
    \tl_use:N \l__forti_table_temp_tl
    \int_step_function:nN { \int_use:N \l__forti_table_ncols_int }
      \__forti_typeset_table_item:n
    \\
  }
\cs_new_protected:Npn \__forti_typeset_table_item:n #1
  {
    \seq_gpop:NN \g__forti_table_body_seq \l__forti_table_temp_tl
    \use:x { \c_alignment_token \exp_not:V \l__forti_table_temp_tl }
  }
\msg_new:nnn { forti } { wrong-number-of-items }
  { Wrong~number~of~items~`#2'.~The~table~has~`#1'~cells. }
\ExplSyntaxOff

\begin{document}

\cards{ lente | zomer | herfst | winter }
      { B | G | D | S }
      {3|1|6|1|
       2|2|3|2|
       4|3|4|3|
       5|7|5|4}

\cards[sep = /, align = l]
  { foo / bar / baz }
  { F   / B   / B }
  {1/2/3/
   4/5/6/
   7/8/9}

\end{document}

And as requested, here's a version without the first column:

enter image description here

\documentclass{article}
\usepackage{booktabs}
\usepackage{xparse}

\ExplSyntaxOn
\tl_new:N \l__forti_table_sep_tl
\tl_new:N \l__forti_table_align_tl
\tl_new:N \l__forti_table_temp_tl
\int_new:N \l__forti_table_ncols_int
\seq_new:N \g__forti_table_body_seq
\seq_new:N \g__forti_table_header_seq
\cs_generate_variant:Nn \seq_gset_split:Nnn { Nx }
\keys_define:nn { forti }
  {
    , sep .tl_set:N = \l__forti_table_sep_tl
    , sep .initial:n = { | }
    , sep .value_required:n = true
    , align .tl_set:N = \l__forti_table_align_tl
    , align .initial:n = { c }
    , align .value_required:n = true
  }
\NewDocumentCommand { \cards } { o m m }
  {
    \group_begin:
      \IfValueT {#1} { \keys_set:nn { forti } {#1} }
      \forti_card_distribution:nn {#2} {#3}
    \group_end:
  }
\cs_new_protected:Npn \forti_card_distribution:nn #1 #2
  {
    \__forti_seq_gset_split:Nn \g__forti_table_header_seq {#1}
    \__forti_seq_gset_split:Nn \g__forti_table_body_seq   {#2}
    \__forti_card_distribution_check_do:nnN
      { \seq_count:N \g__forti_table_header_seq }
      { \seq_count:N \g__forti_table_body_seq }
    \__forti_card_distribution:nn
  }
\cs_new_protected:Npn \__forti_seq_gset_split:Nn #1 #2
  { \seq_gset_split:Nxn #1 { \tl_use:N \l__forti_table_sep_tl } {#2} }
\cs_new_protected:Npn \__forti_card_distribution_check_do:nnN #1 #2 #3
  {
    \int_compare:nNnTF { \int_mod:nn {#2}{#1} } = { 0 }
      { #3 {#1} {#2} }
      { \msg_error:nnxx { forti } { wrong-number-of-items } {#1} {#2} }
  }
\cs_new_protected:Npn \__forti_card_distribution:nn #1 #2
  {
    \int_set:Nn \l__forti_table_ncols_int {#1}
    \use:x
      {
        \exp_not:N \begin{tabular}
          { * { \int_eval:n {#1} } { \tl_use:N \l__forti_table_align_tl } }
      }
      \toprule
        \seq_use:Nn \g__forti_table_header_seq { \c_alignment_token } \\
      \midrule
        \int_step_function:nN { #2/#1 }
          \__forti_typeset_table_row:n
      \bottomrule
    \end{tabular}
  }
\cs_new_protected:Npn \__forti_typeset_table_row:n #1
  {
    \int_step_function:nN { \l__forti_table_ncols_int }
      \__forti_typeset_table_item:n
    \\
  }
\cs_new_protected:Npn \__forti_typeset_table_item:n #1
  {
    \seq_gpop:NN \g__forti_table_body_seq \l__forti_table_temp_tl
    \use:x
      {
        \int_compare:nNnT {#1} > { 1 } { \c_alignment_token }
        \exp_not:V \l__forti_table_temp_tl
      }
  }
\msg_new:nnn { forti } { wrong-number-of-items }
  { Wrong~number~of~items~`#2'.~The~table~has~`#1'~cells. }
\ExplSyntaxOff

\begin{document}

\cards{ lente | zomer | herfst | winter }
      {3|1|6|1|
       2|2|3|2|
       4|3|4|3|
       5|7|5|4}

\cards[sep = /, align = l]
  { foo / bar / baz }
  {1/2/3/
   4/5/6/
   7/8/9}

\end{document}