# How can I determine the size of an array containing macros with optional parameters?

I am using my solution from How can I determine the size of an array? to count the number of entries. However, the \foreach/\IfStrEq in \SetArrayLength seems to have a problem when an entry is macro (\Ellipsis in the MWE) with an optional parameter. I naively thought that would be simple to fix by just redefining the macro locally:

\renewcommand{\Ellipsis}[1][]{z}%

but this still does not let me count the number of entries of \ListB nor \ListC. The resultant error message is

Undefined control sequence.
\@xs@IfStrEq@@ ...\@xs@arg@ii {#2}\edef \@xs@call
{\noexpand \@xs@IfStrEq {\...

## Notes:

• This is a greatly simplified version of what I have so much of the usefulness is lost here.

## Code:

\documentclass{article}
\usepackage{tikz}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{xcolor}

%%% https://tex.stackexchange.com/questions/100542/how-to-extract-the-name-of-a-macro
\ExplSyntaxOn
\newcommand{\CsToStr}[1]{\cs_to_str:N #1}
\ExplSyntaxOff

\makeatletter

\newcounter{@LengthOfArray}%
\newcommand{\GetArrayLength}{\arabic{@LengthOfArray}}%
\newcommand*{\SetArrayLength}[1]{%
% https://tex.stackexchange.com/questions/66121/how-can-i-determine-the-size-of-an-array
%
% Counts the number of array members. This ignore any empty
% array members created by extraneous commas (such as those
% as  result of a double comma, or a trailing comma.
%
\setcounter{@LengthOfArray}{0}%
\begingroup
\renewcommand{\Ellipsis}[1][]{z}% <-- Redefine locally for counting
\foreach \x in #1 {%
\IfStrEq{\x}{}{}{%% only increment when non-empty
\stepcounter{@LengthOfArray}%
}%
}%
\endgroup
}%

\newcommand*{\Ellipsis}[1][\dots]{%
\,#1\ltx@ifnextchar{,}{\,}{}%
}%
\makeatother

\newcommand*{\ListA}{1,2,3,,,,4,}
\newcommand*{\ListB}{1,2,3,,,,4,\Ellipsis}
\newcommand*{\ListC}{1,2,3,,,,4,\Ellipsis,5}

\newcommand*{\CheckLength}[2]{%
% #1 = string macro
% #2 = expected length
\SetArrayLength{#1}%
Length of \CsToStr{#1} is \GetArrayLength%
\ifnum\GetArrayLength=#2\relax
~as expected.
\else
, \textcolor{red}{but should have been #2!!}
\fi
}%

\begin{document}

%Test \verb|\Ellipsis:|
%$1, \Ellipsis.$   \quad
%$1, \Ellipsis[\cdots], 2$
%\bigskip

\CheckLength{\ListA}{4}\par %% This is ok, but following two have issues.
\CheckLength{\ListB}{5}\par
\CheckLength{\ListC}{6}

\end{document}
• Why the tikz tag? – Alenanno Apr 30 '16 at 22:23
• @Alenanno \foreach and PGF maths stuff ? – cfr Apr 30 '16 at 22:24
• @cfr The \foreach can be done with pgffor as well no? And I didn't see any \pgfmath command in the code above, so that's why I thought tikz was weird there. :D – Alenanno Apr 30 '16 at 22:28
• @Alenanno I'm guessing it is a feature of the larger document rather than the minimal example since tikz is loaded rather than pgffor. And pgffor is, after all, part of PGF/TikZ, so the tikz-pgf tag is not inaccurate. – cfr Apr 30 '16 at 22:57

The usual expansion problems with xstring, I'd say. But you're complicating things. The \clist_set:Nn function of expl3 purges empty items, so it's a straightforward application.

\documentclass{article}
\usepackage{xparse}
\usepackage{xcolor}

\ExplSyntaxOn
\NewDocumentCommand{\SetArrayLength}{sm}
{
\IfBooleanTF{#1}
{% star form: expect a control sequence
\grill_array_length:V #2
}
{
\grill_array_length:n { #2 }
}
}
\DeclareExpandableDocumentCommand{\GetArrayLength}{}
{
\clist_count:N \l_grill_purged_array_clist
}
\clist_new:N \l_grill_purged_array_clist
\cs_new_protected:Nn \grill_array_length:n
{
\clist_set:Nn \l_grill_purged_array_clist { #1 }
}

\cs_generate_variant:Nn \grill_array_length:n { V }
\ExplSyntaxOff

\makeatletter
\newcommand*{\Ellipsis}[1][\dots]{%
\,#1\ltx@ifnextchar{,}{\,}{}%
}
\makeatother

\newcommand*{\ListA}{1,2,3,,,,4,}
\newcommand*{\ListB}{1,2,3,,,,4,\Ellipsis}
\newcommand*{\ListC}{1,2,3,,,,4,\Ellipsis,5}

\newcommand*{\CheckLength}[2]{%
% #1 = string macro
% #2 = expected length
\SetArrayLength*{#1}%
Length of \texttt{\string#1} is \GetArrayLength
\ifnum\GetArrayLength=#2\relax
~as expected.
\else
, \textcolor{red}{but should have been #2!!}
\fi
}%

\begin{document}

%Test \verb|\Ellipsis:|
%$1, \Ellipsis.$   \quad
%$1, \Ellipsis[\cdots], 2$
%\bigskip

\CheckLength{\ListA}{4}\par %% This is ok, but following two have issues.
\CheckLength{\ListB}{5}\par
\CheckLength{\ListC}{6}

\end{document}

You could do

\let\Ellipsis\relax

instead of \renewcommand{\Ellipsis}[1][]{z}, but with the above strategy you don't care about what items you have in the list.

The correct strategy with xstring is \noexpandarg:

\newcommand*{\SetArrayLength}[1]{%
% http://tex.stackexchange.com/questions/66121/how-can-i-determine-the-size-of-an-array
%
% Counts the number of array members. This ignore any empty
% array members created by extraneous commas (such as those
% as  result of a double comma, or a trailing comma.
%
\setcounter{@LengthOfArray}{0}%
\begingroup
\noexpandarg
\foreach \x in #1 {%
\expandafter\IfStrEq\expandafter{\x}{}{}{%% only increment when non-empty
\stepcounter{@LengthOfArray}%
}%
}%
\endgroup
}