4

I've assigned an array and I want to design a command (Assume it's named \arraynum) with 2 values to print the number of elements in the first and second rows of an array respectively, for example

\documentclass{article}
\usepackage{tikz}

\makeatletter
\ExplSyntaxOn
\cs_set:Npn \MyArray #1 {
  \gdef\@MyArray{#1}
  \gdef\@Arraynum{\clist_count:n {#1}}
  \seq_set_split:Nnn \l_node_row_seq {;} {#1}
  \int_step_inline:nn{\seq_count:N \l_node_row_seq}
  {
    \seq_if_exist:cF {l_node_row_##1_seq}
    {
      \seq_new:c {l_node_row_##1_seq}
    }
    \exp_args:Ncx\seq_set_from_clist:Nn {l_node_row_##1_seq} {\seq_item:Nn \l_node_row_seq{##1}}
  }
}
\ExplSyntaxOff

\begin{document}

\MyArray{
    1,2,3,4,5,6,7;
    $\alpha$,$\beta$,$\gamma$,$\delta$,$\epsilon$
}

There are \@Arraynum{} elements in the array, 7 elements in the first line while 5 elements in the second line.

\end{document}

There are 7 elements in the first line, so the command \arraynum[1]{}the command \arraynum[1]{@MyArray} should print 7 while the command \arraynum[2]{@MyArray} should print 5. So, how can I design this command?

Whats's more, as you can see, there are 12 elements in the array in total, while the first line and the second line were separated by the symbol ;, but the command \clist_count:n {\@MyArray} couldn't recognize the separate symbol ; so it print 11 while the correct number is 12. How can I solve these problems?

2 Answers 2

2

You can make better use of expl3 so you can define as many arrays as you want and retrieve information from them in a uniform way.

As a general rule, don't mix \gdef with expl3.

Note that the “step 2” commands below are fully expandable.

Step 1. Set up an infrastructure for arrays.

Step 2. Define commands to work with these arrays, for instance “count items” or “retrieve one item”.

Here's the code: the array is saved as a sequence of clists.

\documentclass{article}

\ExplSyntaxOn

% step 1, set up arrays
\NewDocumentCommand{\definearray}{mm}
 {
  \seq_clear_new:c { l__axia_array_#1_seq }
  \seq_set_split:cnn { l__axia_array_#1_seq } { ; } { #2 }
 }

\cs_generate_variant:Nn \seq_set_split:Nnn { c }

% step 2, define commands to work with arrays
\NewExpandableDocumentCommand{\arraycount}{som}
 {
  \IfBooleanTF { #1 }
   {
    \seq_count:c { l__axia_array_#3_seq }
   }
   {
    \IfNoValueTF { #2 }
     {
      \axia_array_count_full:n { #3 }
     }
     {
      \axia_array_count_item:nn { #3 } { #2 }
     }
   }
 }

\cs_new:Nn \axia_array_count_full:n
 {
  \int_eval:n
   {
    0
    \seq_map_function:cN { l__axia_array_#1_seq } \__axia_array_count:n
   }
 }
\cs_new:Nn \__axia_array_count:n
 {
  + \clist_count:n { #1 }
 }

\cs_new:Nn \axia_array_count_item:nn
 {
  \clist_count:e { \seq_item:cn { l__axia_array_#1_seq } { #2 } }
 }

\NewExpandableDocumentCommand{\arrayitem}{mmm}
 {
  \clist_item:en { \seq_item:cn { l__axia_array_#1_seq } { #2 } } { #3 }
 }

\ExplSyntaxOff

\begin{document}

\definearray{MyArray}{
  1,2,3,4,5,6,7;
  $\alpha$,$\beta$,$\gamma$,$\delta$,$\epsilon$
}

There are \arraycount{MyArray} elements in the array, 
\arraycount[1]{MyArray} elements in the first line and
\arraycount[2]{MyArray} elements in the second line.

There are \arraycount*{MyArray} lines in the array.

The third item in the second line is \arrayitem{MyArray}{2}{3}.

\end{document}

You see that \arraycount computes the full length; if you pass an optional argument you get the length of the corresponding row; instead, \arraycount*{<name>} computes the number of lines.

enter image description here

You can test full expandability by doing

\edef\testA{\arraycount*{MyArray}}
\edef\testB{\arraycount{MyArray}}
\edef\testC{\arraycount[1]{MyArray}}

\texttt{\meaning\testA}\par
\texttt{\meaning\testB}\par
\texttt{\meaning\testC}

enter image description here

You can do, in the same document,

\definearray{Another}{aaa,bbb,ccc;ddd,eee;fff,ggg,hhh;iiii}

\arraycount{Another}

\arraycount*{Another}

\arraycount[1]{Another}--%
\arraycount[2]{Another}--%
\arraycount[3]{Another}--%
\arraycount[4]{Another}

and you'll get

enter image description here

5
  • The problem has been solved! Thank you very much!
    – Axia
    Feb 28 at 0:56
  • Oh, by the way, I'd like to know how can I use \@. Such as you set up an array MyArray, then, how can I use \@MyArray after I define it?
    – Axia
    Feb 28 at 9:47
  • And what I can think is to \gdef\@#1{#2} in step 1, means I'd like to create a value named \@#1 and pass #2 to the value \@#1 (I know I should not use the command \gdef), however it doesn't work. How can I reach this effect?
    – Axia
    Feb 28 at 9:54
  • Or I can say I'd like to print the array again after I set up it...😂
    – Axia
    Feb 28 at 9:57
  • 1
    @Axia Commands with @ in their name are only legal between \makeatletter and \makeatother; also you can't do \def\@#1{#2} (or, strictly speaking, you can, but it doesn't do what you want). About “printing“, knowing what you want to do with these arrays is necessary. There are several ways, which one to choose depends on the intended application.
    – egreg
    Feb 28 at 10:02
5

Using listofitems, instead of expl3:

\documentclass{article}
\usepackage{listofitems}
\newtoks\zz
\newcommand\addtozz[1]{\global\zz\expandafter{\the\zz#1}}
\newcommand\xaddtozz[1]{\expandafter\addtozz\expandafter{#1}}
\newcommand\xxaddtozz[1]{\expandafter\xaddtozz\expandafter{#1}}
\setsepchar{;/,}
\newcommand\TotalElements{\zz{}%
  \foreachitem\z\in\MyArray[]{%
    \ifnum\zcnt=1 \else\addtozz{+}\fi
    \xxaddtozz{\listlen\MyArray[\zcnt]}%
  }%
  \the\numexpr\the\zz\relax
}    
\begin{document}
\readlist*\MyArray{
    1,2,3,4,5,6,7;
    $\alpha$,$\beta$,$\gamma$,$\delta$,$\epsilon$
}

There are \TotalElements{} elements in the array, \listlen\MyArray[1] elements 
in the first line while \listlen\MyArray[2] elements in the second line.
\end{document}

enter image description here

Since counters are global, it would actually be easier to use them, in preference to a token list, to count the \TotalElements:

\documentclass{article}
\usepackage{listofitems}
\newcounter{zz}
\setsepchar{;/,}
\newcommand\TotalElements{\setcounter{zz}{0}%
  \foreachitem\z\in\MyArray[]{%
    \addtocounter{zz}{\listlen\MyArray[\zcnt]}%
  }%
  \thezz
}    
\begin{document}
\readlist*\MyArray{
    1,2,3,4,5,6,7;
    $\alpha$,$\beta$,$\gamma$,$\delta$,$\epsilon$
}

There are \TotalElements{} elements in the array, \listlen\MyArray[1] elements 
in the first line while \listlen\MyArray[2] elements in the second line.
\end{document}

You must log in to answer this question.

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