Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In this answer we learnt how to remove the n-th element from a comma-separated list.

How can I remove an element from a list by its value, rather than its position? For example: if my global variable \citylist currently has the value derby,leicester,nottingham, then after executing \removecity{leicester} I would like it to have the value derby,nottingham.

I already have an \addcity macro (which I made by adapting a macro by David Carlisle):

\newcommand\addcity[1]{%
  \ifx\citylist\@empty\else\g@addto@macro\citylist{,}\fi
  \g@addto@macro\citylist{#1}
}
share|improve this question

2 Answers 2

up vote 9 down vote accepted

In simple cases, you can use LaTeX's \@removeelement:

\documentclass{article}

\newcommand*{\citylist}{derby,leicester,nottingham}

\makeatletter
\@expandtwoargs\@removeelement{leicester}\citylist\citylist
\makeatother

\typeout{\citylist}

\begin{document}
\end{document}

Result:

derby,nottingham

Simple means, the list is normalized without optional spaces around the comma. The list elements does not contain commas and macros.

A variant with package kvsetkeys:

\documentclass{article}

\usepackage{kvsetkeys}

\makeatletter
\newcommand*{\addcity}[1]{%
  \ifx\citylist\@empty\else\g@addto@macro\citylist{,}\fi
  \g@addto@macro\citylist{#1}
}

\newcommand*{\removecity}[1]{%
  \def\unwanted@city{#1}%
  \edef\citylist{\expandafter}%
  \expandafter\comma@parse\expandafter{\citylist}\@removecity
}
\newcommand*{\@removecity}[1]{%
  \def\param@city{#1}%
  \ifx\param@city\unwanted@city
  \else
    \addcity{#1}%
  \fi
}
\makeatother

\newcommand*{\citylist}{derby,leicester,nottingham}
\removecity{leicester}
\typeout{\citylist}

\begin{document}
\end{document}

Then spaces do not disturb, e.g.:

\newcommand*{\citylist}{derby, leicester, nottingham}

The city names may contain commands and fragile macros, they are not expanded.

With the following definition for \addcity, the city names might even contain commas:

\newcommand*{\addcity}[1]{%
  \ifx\citylist\@empty\else\g@addto@macro\citylist{,}\fi
  \g@addto@macro\citylist{{#1}}
}

Example for a list:

{derby},{leicester},{berlin, west},{berlin, east}

share|improve this answer

The expl3 suite can manage comma separated lists in many ways.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\createlist}{mm}
 {
  \clist_new:c { g_wickerson_data_#1_clist }
  \clist_gset:cn { g_wickerson_data_#1_clist } { #2 }
 }
\NewDocumentCommand{\addtolist}{mm}
 {
  \clist_gput_right:cn { g_wickerson_data_#1_clist } { #2 }
 }
\NewDocumentCommand{\removefromlist}{mm}
 {
  \clist_gremove_all:cn { g_wickerson_data_#1_clist } { #2 }
 }
\NewDocumentCommand{\purgelist}{m}
 {
  \clist_gremove_duplicates:c { g_wickerson_data_#1_clist }
 }
\NewDocumentCommand{\printlist}{m}
 {
  \clist_use:cnnn { g_wickerson_data_#1_clist } { ,~ } { ,~ } { ,~ }
 }
\cs_generate_variant:Nn \clist_use:Nnnn { c }
\ExplSyntaxOff

%%% working with a specific list without needing two args
\newcommand{\addcity}[1]{\addtolist{city}{#1}}
\newcommand{\removecity}[1]{\removefromlist{city}{#1}}
\newcommand{\printcity}{\printlist{city}}

\createlist{city}{derby,leicester} % initialization

\begin{document}

\addcity{nottingham}

Cities: \printcity

\bigskip

\removecity{leicester}

Cities: \printcity

\end{document}

However, comma lists are not the most efficient data type: sequences are better. And the internal implementation shouldn't be a concern.

The same is achieved in a faster way with

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\createlist}{mm}
 {
  \seq_new:c { g_wickerson_data_#1_seq }
  \seq_gset_from_clist:cn { g_wickerson_data_#1_seq } { #2 }
 }
\NewDocumentCommand{\addtolist}{mm}
 {
  \seq_gput_right:cn { g_wickerson_data_#1_seq } { #2 }
 }
\NewDocumentCommand{\removefromlist}{mm}
 {
  \seq_gremove_all:cn { g_wickerson_data_#1_seq } { #2 }
 }
\NewDocumentCommand{\purgelist}{m}
 {
  \seq_gremove_duplicates:c { g_wickerson_data_#1_seq }
 }
\NewDocumentCommand{\printlist}{m}
 {
  \seq_use:cnnn { g_wickerson_data_#1_seq } { ,~ } { ,~ } { ,~ }
 }
\cs_generate_variant:Nn \seq_use:Nnnn { c }
\ExplSyntaxOff

%%% working with a specific list without needing two args
\newcommand{\addcity}[1]{\addtolist{city}{#1}}
\newcommand{\removecity}[1]{\removefromlist{city}{#1}}
\newcommand{\printcity}{\printlist{city}}

\createlist{city}{derby,leicester} % initialize

\begin{document}

\addcity{nottingham}

Cities: \printcity

\bigskip

\removecity{leicester}

Cities: \printcity

\end{document}
share|improve this answer
    
Thanks very much @egreg. Unfortunately, when I run your first program, I get the error "Command '\clist_use:Nnnn' not yet defined!", and from your second program I get "Command '\seq_use:Nnnn' not yet defined!". I'm running pdfTeX, Version 3.1415926-2.3-1.40.12 (TeX Live 2011) -- do I need to update? –  John Wickerson Apr 19 '13 at 10:07
1  
@JohnWickerson You definitely need to update. TeX Live 2011 is very outdated with respect to expl3. Note that TL 2013 is in testing phase, but it's surely worthy to upgrade to TL 2012. –  egreg Apr 19 '13 at 10:10
    
Thanks @egreg. I'm now on pdfTeX Version 3.1415926-2.4-1.40.13 (TeX Live 2012), but I still get an error, albeit now with a slightly more fashionable wording: "Control sequence \clist_use:Nnnn undefined." –  John Wickerson Apr 19 '13 at 10:24
    
sounds more than strange. can you post the log file somewhere? –  Frank Mittelbach Apr 19 '13 at 13:25
    
@JohnWickerson You have to run tlmgr and update fully. –  egreg Apr 19 '13 at 15:01

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.