20

LaTeX's low-level programming is poorly documented and the section on what is called control commands is even more so.

LaTeX provides the \@for macro. This works by repeatedly assigning list items to a temporary variable:

I want to define a list that will include all the greek math letters. The list is defined as follows:

\newcommand{\mathList}{\alpha,\beta,\gamma,
               \delta,\epsilon,\zeta,\theta}

To iterate over the list I have used the @for macro. For example the following explodes the list and removes the comma.

   \@for\i:=\mathList\do{%
      \ensuremath \i \space 
    }

How can I define a macro to be able to delete the nth element of the list? I have figured out appending to the list but have not posted it for brevity. I would prefer a TeX or LaTeX solution, although I would also be curious to see how it is done in LaTeX3, so all solutions are welcome.

Minimal example for convenience below:

\documentclass[11pt]{article}
\begin{document}
\makeatletter
\let\dotlessi\i
\newcommand*{\mathList}{\alpha,\beta,\gamma,
               \delta,\epsilon,\zeta,\theta, }
\@for\i:=\mathList\do{%
  \ensuremath \i \space 
 }
\let\i\dotlessi
\makeatother
\end{document}
8
  • 6
    Can we allow @ in tags please?
    – yannisl
    Commented Nov 21, 2010 at 18:22
  • 1
    Have you looked at etextools? tug.ctan.org/cgi-bin/ctanPackageInformation.py?id=etextools It has a lot of CSV manipulation stuff in it, so maybe you don't need to re-invent the wheel. :) Commented Nov 21, 2010 at 18:34
  • @Willie Thanks. I know of the package, but I am half-way reading through source2e trying to boost by understanding of TeX. Datatool can probably achieve it also.
    – yannisl
    Commented Nov 21, 2010 at 18:43
  • Tangentially: some operations are more easily done on \@elt-lists, i.e. \@elt{\alpha}\@elt{\beta} etc., by defining \@elt appropriately and executing of \edefing the list. Commented Nov 21, 2010 at 21:57
  • 1
    In ConTeXt, you can use \addtocommalist to add an element to a comma list (if it does not exist already), \removefromcommalist to remove an element, \appendtocommalist and \prependtocommalist to append an element (without checking if it is already there), \getfromcommalist to get the n-th element from a comma list, etc. If you are interested in a ConTeXt solution, I can post more details.
    – Aditya
    Commented Nov 21, 2010 at 22:03

3 Answers 3

14

Here is one way (deleting item number 3 from the list):

\count@=0
\toks@{}
\@for\i:=\mathList\do{%
  \advance\count@ 1
  \ifnum\count@=3 \else
    \edef\0{\the\toks@\expandafter\noexpand\i,}%
    \toks@\expandafter{\0}%
  \fi
 }%
\edef\mathList{\the\toks@}
4
  • Good answer. Are \count@ and \toks@ registers LaTeX creates for local use like this? Or do you have to declare them earlier? Commented Nov 22, 2010 at 10:38
  • And what is \0? Commented Nov 22, 2010 at 10:40
  • @Matthew: \count@ and \toks@ are temporary registers for local use. They originated in plain TeX and LaTeX includes them as well. (LaTeX also has a number of other temporary registers of various types.) For completeness, \toks@ is just \toks0 and \count@ is \count255 (\count0 is the page number). \0 is just a control symbol that is being defined. It's not anything special.
    – TH.
    Commented Nov 22, 2010 at 11:36
  • 1
    Good answer. It needs a minor amendment though in order not to add an extra comma at the end. This can be removed by checking if the temp variable is empty before defining the `\0'. My macro for adding an item does not use token registers.
    – yannisl
    Commented Nov 22, 2010 at 12:04
10

In expl3, you would write

 \clist_remove_element:Nn \l_tmpa_clist {foo}

to remove foo from the comma-list variable \l_tmpa_clist. There is also a global \clist_gremove_element:Nn. The technique used is much the same as posted by Harald: the list is iterated over, and if the element is not matched then it is pushed onto a new clist.

Update: In the spirit of Aditya's summary of clist methods for ConTeXt, here's a few more expl3 examples (this covers the broad features but doesn't touch on everything). To initialise:

\clist_new:N \l_tmpa_clist
\clist_clear:N \l_tmpa_clist

To add data:

\clist_put_left:Nn \l_tmpa_clist {a}
\clist_put_right:Nn \l_tmpa_clist {b}

To iterate/map over:

\clist_map_inline:Nn \l_tmpa_clist { this is element: #1 \\ }
\clist_map_inline:nn {a,b,c} { this is element: #1 \\ }

Querying:

\clist_if_in:NnTF \l_tmpa_clist {a} {true} {false} 

Manipulating:

\clist_remove_duplicates:N \l_tmpa_clist
\clist_remove_element:Nn \l_tmpa_clist {c}

Of course, you don't have to follow the expl3 naming scheme for variables; in scratch code it's perfectly fine to write \foo instead of \l_tmpa_clist.

Comma-lists in expl3 generally do not contain empty elements, and spaces around elements are preserved (this is an arguable point that might change slightly in the future). They can contain anything except un-protected commas.

expl3 also provides a structure called a "sequence" that are used similarly to clists but are intended to be used when only internal functions are adding to/manipulating the data; comma-lists as a data structure are only really necessary when they inherit their data from document-level input. (The advantage to using sequences are slightly more generic processing and you can include commas in their elements.)

4
  • FWIW, the ConTeXt implementation of removing the element does not iteate over the list. See the definition of removefromcommalist in mult-aux.mkiv.
    – Aditya
    Commented Nov 22, 2010 at 21:46
  • @Aditya there are two ways to remove an element. One is by referring to it by position say \remove{3}{\alist} (Harald's solution) or by name. The more common method is to refer to it by name \remove{\alpha}{\alist}'. In the latter since we can capture the beginning of the list up to the deletion in an argument #1,#2,#3, iteration is unecessary. One can also define variables on the fly to store the position in the list, which can solve the first case without iteration. I guess it all depends on the interface. If one would say use \additem{} to build a list no iteration is necessary.
    – yannisl
    Commented Nov 22, 2010 at 22:43
  • @Yiannis: This is similar to how the corresponding macros are implemented in ConTeXt. And that is why I was surprised by Will's remark that "the list is iterated over, and if the element is not matched then it is pushed onto a new list"
    – Aditya
    Commented Nov 23, 2010 at 16:06
  • @Aditya The iteration approach is more robust when your clist contains arbitrary material involving braces etc. Which is arguably not material fit for a clist, but anyway — \def\clist{aa,a{b}c,cc} \removefromcommalist{a{b}c}\clist fails. Commented Nov 24, 2010 at 2:25
8

Here is a summary of different comma list manipulation commands in ConTeXt.

This is how I would do this in ConTeXt.

Define the list

\def\MathList{alpha,beta,gamma,delta,epsilon,zeta,theta}

ConTeXt does not remove spaces from each element of the list. This {alpha, beta} is different from {alpha,beta}.

Next define a macro to display a list element

\def\ShowListElement#1{\mathematics{\getvalue{#1}}\space}

To display the entire list:

\processcommacommand[\MathList]\ShowListElement

To add an element to the end of the list:

\addtocommalist {phi} \MathList
\processcommacommand[\MathList]\ShowListElement

To add an element to the beginning of the list:

\pretocommalist {kappa} \MathList
\processcommacommand[\MathList]\ShowListElement

The element is added only if does not exist already.

\addtocommalist {alpha} \MathList
\processcommacommand[\MathList]\ShowListElement

To delete an element from the list:

\removefromcommalist {gamma} \MathList
\processcommacommand[\MathList]\ShowListElement

To add an element to a list (without checks):

\appendtocommalist  {nu} \MathList
\prependtocommalist {nu} \MathList
\processcommacommand[\MathList]\ShowListElement

To substitute the value of an element:

\substituteincommalist {delta} {Delta} \MathList
\processcommacommand[\MathList]\ShowListElement

To get the size of a comma list

\getcommacommandsize [\MathList]
\commalistsize

Get indexed element from comma list

\getfromcommacommand [\MathList] [2]
\commalistelement

Replace an element from comma list

\def\newcommalistelement {iota}
\replaceincommalist \MathList {2}
\commalistelement \crlf
\processcommacommand[\MathList]\ShowListElement

To reverse a comma list. The result is stored in \reversedlist

\reversecommacommand\MathList
\processcommacommand[\reversedlist]\ShowListElement

Each command that uses commacommand also has a commalist variant that can be used if the list is not stored in a macro (for example, while processing key value lists).

There are a few other functions that are specific to numeric lists (sorting, compressing, expanding, etc).

You must log in to answer this question.

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