7

This is probably a very basic question, apologies for lacking better knowledge of LaTeX syntax.

I want to use \cftpagenumbersoff from the tocloft package to hide the page numbers (and dots) of some (but not all) sectioning levels, say section and subsection.

I know that in the preamble I could write

\usepackage{tocloft}
\cftpagenumbersoff{section}
\cftpagenumbersoff{subsection}

But I was wondering if I could write something like

\usepackage{tocloft}
\cftpagenumbersoff{section,subsection}

instead (which does not work). I know this only saves me a single line, yet I would love to know what the correct (and efficient) way of putting this would be - if it is possible. Thanks!


Just to explain my motivation: I saw an example where

\uspepackage[titles,subfigure]{tocloft}

was used, so I thought it might also be possible to have multiple arguments in the {} part, e.g. separated by a comma.

5
  • Package options are implemented in a way that allows them to be in a comma separated list (and depending on the package, key/value pairs). But since the author of tocloft has not implemented the \cftpagenumbersoff to work that way, the code you'd need to add to allow it would be more than the extra lines needed to use the command for multiple document subdivisions.
    – Alan Munn
    Commented Jun 26, 2013 at 13:22
  • @AlanMunn Okay, so my first attempt does not work. But is there any other (general?) way to apply some command to several arguments? I also tried e.g. \cftpagenumbersoff{{section},{subsection}}, etc., but to no avail. Or is the answer to my question simply that I do have to write the command twice?
    – Bernd
    Commented Jun 26, 2013 at 13:30
  • Unless the command is designed to work that way, there is no way. It's not hard to create a new command that takes a list and applies the command to the list elements (see Parse comma-separated list of command names with spaces for an example), but for most existing commands in a package this is probably not worth the effort.
    – Alan Munn
    Commented Jun 26, 2013 at 13:32
  • @AlanMunn Thanks! I guess this pretty much answers the question with a "no". Hence, should I delete my question (not really helpful to anyone?), or accept your comment once turned into an answer, or just leave it for the unlikely event that someone comes up with a "yes"?
    – Bernd
    Commented Jun 26, 2013 at 13:35
  • I've turned the comments into an answer. I would wait a day or so before accepting it in case someone else comes up with a different answer.
    – Alan Munn
    Commented Jun 26, 2013 at 14:03

3 Answers 3

7

In simple cases like this one it actually is a one-liner to call a macro for each element of a comma-separated list: \forcsvlist{\cftpagenumbersoff}{subsection,section} is all you need.

The \forcsvlist{<handler>}{<csv list>} command is provided by the etoolbox package. It takes as first argument a handler, which has to be a macro (or sequence of macros) that absorbs exactly one trailing argument and is invoked for each element of the csv list.

\documentclass{report}
\usepackage{tocloft}
\usepackage{etoolbox}

\forcsvlist{\cftpagenumbersoff}{subsection,section}

\begin{document}

\tableofcontents
\chapter{A chapter title}

\section{A section title}
\subsection{A subsection title}
\end{document}
1
  • Nice. I'd forgotten about that version. Thanks for adding it.
    – Alan Munn
    Commented Jun 26, 2013 at 16:46
6

Unless a command has been written to accept a comma separated list of arguments, there's no general way to get it to accept one without rewriting the command, which is likely to be more work than just applying the command multiple times.

It's not that difficult to create a new command that takes a comma separated list and then applies another command to that list, but for most existing commands, this is probably not worth the effort. Adapting the accepted answer from here Parse comma-separated list of command names with spaces to your problem, we could do the following:

\documentclass{report}
\usepackage{tocloft}
\usepackage{etoolbox}

\newcommand\docftpagenumbersoff[1]{\cftpagenumbersoff{#1}}
\newcommand*\mycftpagenumbersoff{
  \let\do\docftpagenumbersoff
  \docsvlist
}
\mycftpagenumbersoff{subsection,section}
\begin{document}

\tableofcontents
\chapter{A chapter title}

\section{A section title}
\subsection{A subsection title}
\end{document}
5

I too believe you're better using two commands; but, just for showing the power of expl3, here's a (complicated) solution for \cftpagenumbersoff (and also for the complementary \cftpagenumberson)

\documentclass{book}
\usepackage{tocloft,xparse}

\ExplSyntaxOn

% A table of equivalents: \cftchapname (=chapter) -> chap and so on
\prop_new:N \g_cft_names_prop
\cs_generate_variant:Nn \prop_gput:Nnn { Nv }
\clist_map_inline:nn
 {chap,sec,subsec,subsubsec,para,subpara,fig,subfig,tab,subtab}
 {
  \prop_gput:Nvn \g_cft_names_prop { cft#1name } { #1 }
 }

\NewDocumentCommand{\xcftpagenumbersoff}{m}
 {
  \clist_map_inline:nn { #1 } { \cft_pagenumbersoff:n { ##1 } }
 }

% \cftpagenumbersoff is a gigantic sequence of \ifx, using \@cftpnumoff
% If the argument is section, it is compared to all known \cftXname commands
% and action is taken; with a property list it's easier

% First we store a copy of \@cftpnumoff
\cs_set_eq:Nc \cft_pnumoff:n { @cftpnumoff }
% We also define a variant that does complete expansion
\cs_generate_variant:Nn \cft_pnumoff:n { x }
\cs_new_protected:Npn \cft_pagenumbersoff:n #1
 {
  \prop_if_in:NnTF \g_cft_names_prop { #1 }
   {% if the argument is in the table, we get from it the abbreviation (chapter->chap)
    \cft_pnumoff:x { \prop_get:Nn \g_cft_names_prop { #1 } }
   }
   {% otherwise we use directly the argument
    \cft_pnumoff:n { #1 }
   }
 }

\NewDocumentCommand{\xcftpagenumberson}{m}
 {
  \clist_map_inline:nn { #1 } { \cft_pagenumberson:n { ##1 } }
 }

\cs_set_eq:Nc \cft_pnumon:n { @cftpnumon }
\cs_generate_variant:Nn \cft_pnumon:n { x }
\cs_new_protected:Npn \cft_pagenumberson:n #1
 {
  \prop_if_in:NnTF \g_cft_names_prop { #1 }
   {
    \cft_pnumon:x { \prop_get:Nn \g_cft_names_prop { #1 } }
   }
   {
    \cft_pnumon:n { #1 }
   }
 }
\ExplSyntaxOff

\xcftpagenumbersoff{subsection,section}
\begin{document}

\tableofcontents
\chapter{A chapter title}

\section{A section title}
\subsection{A subsection title}

\addtocontents{toc}{\xcftpagenumberson{section,subsection}}

\section{X}
\subsection{Y}

\end{document}

enter image description here


Let's tackle the problem from a more general point of view. Given a macro \foo that takes an argument and does some background action with it, define \xfoo that does the same actions on multiple arguments specified as a comma separated list.

Note that this wouldn't make sense for commands such as \textbf that typeset their argument.

\usepackage{xparse} % access to expl3
\ExplSyntaxOn
\NewDocumentCommand{\xfoo}{m}
 {
  \clist_map_inline:nn { #1 } { \foo {##1} }
 }
\ExplSyntaxOff

would be just enough.

The previous solution could so have simply been written

\ExplSyntaxOn
\NewDocumentCommand{\xcftpagenumbersoff}{m}
 {
  \clist_map_inline:nn { #1 } { \cftpagenumbersoff {##1} }
 }
\ExplSyntaxOff

but I preferred to avoid using too many layers over the basic \@cftpnumoff command, presenting some ideas about LaTeX3 programming.

One might think to save a copy of \foo and redefining it, but this opens new problems, because \foo might be defined with \DeclareRobustCommand and a simple \let\originalfoo\foo wouldn't be sufficient. There would be a way to do it also in this case (with \LetLtxMacro), but in general it's better not to change drastically the syntax of an existing command.

2
  • Wow, thanks for this exhaustive answer. Still, I prefer Daniel's solution for its brevity so I'd accept his answer.
    – Bernd
    Commented Jun 27, 2013 at 7:24
  • Note that as of September 2014, \prop_get:Nn has been renamed to \prop_item:Nn
    – egreg
    Commented Sep 17, 2014 at 17:57

You must log in to answer this question.

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