There are many ways of tackling this problem: which you choose depends on your particular requirements.
Taking the example in the question, the reason for the apparent failure with \clist_map_inline:nn
is that expl3
is very careful not to expand anything 'by accident'. Thus when the argument grabbed is a macro containing a comma-separated list, the code never sees the commas: for all you know this could be a list of one item only which just happens to be a macro itself containing a further list! There is also a difference in expl3
between functions which deal with 'stored' and 'inline' comma lists. Essentially, the idea is that a 'stored' list will already have been sanitized to remove spaces and empty items. So what you need to do is use the inline list function and expand your input once:
\NewDocumentCommand \SortCommaSeparatedList { m }
{ \exp_args:No \clist_map_function:nN {#1} \SortItem }
This will work with both forms of your input as your 'inline' list only contains unexpandable tokens. In general you cannot assume that, so I would say that \SortCommaSeparatedList
should be described as accepting either a macro containing a list, or as accepting a list, but not both.
To avoid expl3
, perhaps the easiest way would be to use LaTeX2e's \@for
along with some space stripping code, again altering as little as possible:
\makeatletter
\newcommand*{\SortCommaSeparatedList}[1]{%
\expandafter\@for\expandafter\@tempa\expandafter:\expandafter=#1\do{%
\edef\@tempa{\expandafter\trim@spaces\expandafter{\@tempa}}%
\expandafter\SortItem\expandafter{\@tempa}%
}
}
% This is expl3's \tl_trim_spaces:n
\def\@tempa#1{%
\newcommand{\trim@spaces}[1]{%
\unexpanded\trim@spaces@aux@i\@mark##1\@nil\@mark#1{}\@mark
\trim@spaces@aux@ii\trim@spaces@aux@iii#1\@nil\trim@spaces@aux@iv\@stop
}
\newcommand{\trim@spaces@aux@i}{}
\long\def\trim@spaces@aux@i##1\@mark#1##2\@mark##3{%
##3%
\trim@spaces@aux@i\@mark##2\@mark#1{##1}%
}
\newcommand{\trim@spaces@aux@ii}{}
\long\def\trim@spaces@aux@ii##1\@mark\@mark##2{%
\trim@spaces@aux@iii##2%
}
\newcommand{\trim@spaces@aux@iii}{}
\long\def\trim@spaces@aux@iii##1#1\@nil##2{%
##2%
##1\@nil
\trim@spaces@aux@iii
}
\newcommand{\trim@spaces@aux@iv}{}
\long\def\trim@spaces@aux@iv##1\@nil##2\@stop{%
\expandafter{\@gobble##1}%
}
}
\@tempa{ }
\makeatother
This again expands the argument once, and this time we do a more awkward loop over every item. The space-trimming code is exactly that in expl3
, but written in a more 'traditional' form. (You could write the loop more efficiently here by using that from expl3
, but that seems like more effort for very little real gain.)
You could go further in a few ways. First, if you are willing to stick with expl3
then you could avoid loading xstring
and do the comparisons using \tl_if_empty:nTF
and so forth. There is also an experimental sorting module which would do the entire job for you! On the other hand, as expl3
requires the \pdfstrcmp
primitive you could use that for the sort, although that will be slightly complicated as it works purely on character codes. Finally of course you could use LuaTeX, and do the sort in Lua (I guess that you want some reasonably general, so that is probably out).
Use a conditional.
\documentclass{article}
\usepackage[tiny,raggedright,uppercase,noindentafter,toctitles,explicit]%
{titlesec}
\usepackage{chngcntr} % change the display of counters
\newcommand*{\newpar}{%
\ifsubsection\else\stepcounter{subsection}\fi
\paragraph{}}
%%% A conditional for knowing whether a \subsection
%%% command has been issued
\newif\ifsubsection
\usepackage{etoolbox}
%%% Set the conditional to true after \subsection
%%% and reset the paragraph counter
\preto{\subsection}{\global\subsectiontrue\setcounter{paragraph}{0}}
%%% Reset it to false after \section
\preto{\section}{\global\subsectionfalse}
\counterwithin*{paragraph}{section}
\makeatletter
\renewcommand\thesection{\arabic{section}.\two@digits\c@subsection}
\renewcommand\thesubsection{\thesection}
\renewcommand\theparagraph{%
\ifsubsection
\arabic{section}.\two@digits\c@subsection.\arabic{paragraph}%
\else
\arabic{section}.\two@digits\c@paragraph
\fi
}
%%% Tables
\counterwithin*{table}{section}
\renewcommand{\thetable}{%
\arabic{section}.\two@digits\c@subsection~(\alph{table})%
}
\makeatother
\setcounter{secnumdepth}{5}
\begin{document}
\section{First Section}\label{sec:first}
\newpar This is a paragraph at subsection level in the first section.\label{intropar:first}
\newpar This is also a paragraph at subsection level in the first section.It comes after
intropar \ref{intropar:first}. \label{intropar:second}
\subsection{First Subsection}\label{ssec:first}
\newpar This is a paragraph at paragraph level in subsection \ref{ssec:first} in section
\ref{sec:first}.\label{par:first}
\newpar This is a paragraph at paragraph level in subsection \ref{ssec:first} in section
\ref{sec:first}. It comes after paragraph \ref{par:first}.\label{par:second}
\subsection{Second Subsection}\label{ssec:second}
\newpar This is a paragraph at paragraph level in subsection \ref{ssec:second} in section
\ref{sec:first}. It comes after paragraphs \ref{par:first} and
\ref{par:second}.\label{par:third}
\section{Second Section}
\newpar This is a paragraph at subsection level in the first section.
\newpar This is also a paragraph at subsection level in the first section.
\subsection{First Subsection}
\newpar This is a paragraph at paragraph level.
\newpar This is a paragraph at paragraph level.
\subsection{Second Subsection}
\newpar This is a paragraph at paragraph level in subsection.
\end{document}
Best Answer
Here's an implementation:
However, a simpler strategy can be easier:
You define your interfaces as before, in the preamble,
and then say
Sorting a list of short commands is easier than sorting big chunks of code.