26

I have the following setup, I have a file with some definitions, one of which is a list of items:

\newcommand{\mylist}{
\item first item
\item second item
\item third item
}

I want to use \mylist in two different ways, first

\begin{itemize}
\mylist
\end{itemize}

and second,

\begin{itemize}
% somehow extract 1st and 3rd items from \mylist
\end{itemize}

or alternatively

\being{itemize}
%somehow extract first n items from \mylist
\end{itemize}

The selection can be done by stating which items I want {1,3}, or, maybe better, using \label{} and \ref{} so that when I add more items, I do not need to change the numbering in the subset.

I need this for keeping an updated list of publications/references/talks in one place, while being able to generate various documents containing a more, or less verbose list of items.


EDIT: Due to the many answers below, I'm keeping a copy of the .sty file I ended up writing and using. Its undocumented, and subject to change...but you can use it if you like. Most of the fiel is due to Ryan's accepted answer, but I added some things thanks to andrew and egreg (as noted in the code).

% A package that allows the selection of items from a list based on
% the id's of the items in the list (good for CV's) based on an answer
% by Ryan Reich on TeX.SE
% http://tex.stackexchange.com/questions/4502/onlyitems-how-to-select-specific-items-from-an-item-list/32611#32611 


%%%%%%%%%%%%%%%%%
\ProvidesPackage{onlyitems}

\RequirePackage{pgfkeys}
\RequirePackage{pgffor}



% A huge list of pgfkeys to be set up.  
\pgfkeys{
 /onlyitems/.is family, /onlyitems,
 % Utility keys
 utility/store tag/.store in = \itemOptions,
 utility/store bool/.store in = \itemIf,
 utility/process true/.style = {process/#1 = \iftrue},
 utility/process false/.style = {process/#1 = \iffalse},%\fi
 utility/set store bool/.style 2 args = {#1/.style = {utility/store bool = #2}},
 utility/set select style/.style 2 args = {utility/set store bool = {select/#1}{#2}},
 utility/verdict/.style = {},
 utility/add to reset/.style = {
  reset/.prefix style = {select/#1/.style = {select/.unknown}}
 },
 utility/current key/.estore in = \itemKey,
 % Nothing in the process family is ever set, so any selection option is set to store a boolean
 process/.unknown/.style = {
  utility/current key = \pgfkeyscurrentname,
  select/\itemKey/.style = {
   utility/set store bool = {utility/verdict}{#1}
  },
  utility/add to reset/.expand once = \itemKey
 },
 % For clearing the list of selected items (to support nested {itemize})
 reset/.style = {},
 reset/reset/.style = {reset/.style = {reset/reset, default}},
 reset/reset,
 % Here are the options you can actually pass.  These go to the {itemize}...
 include/.style = {utility/process true/.list  = {#1}},
 exclude/.style = {utility/process false/.list = {#1}},
 all/.style  = {utility/set select style = {.unknown}{\iftrue}},
 none/.style = {utility/set select style = {.unknown}{\iffalse}},%\fi
 default/.style = {all},
 % ...and these to the \item's
 tag/.style = {utility/store tag = {[#1]}},
 id/.style = {select/#1}, 
%yossi
.unknown/.style = {tag=\pgfkeyscurrentname} %if key is
%.unknown/.code = \onlyitemsset{utility/store tag=\pgfkeyscurrentname} %if key is
                                %unknown, treat it like a tag. thanks
                                %to Andrew Stacy: \url{http://tex.stackexchange.com/q/34376/86}
%end yossi
}

% Like tikzset, it sets a default key path
\newcommand\onlyitemsset[1]{\pgfkeys{/onlyitems, #1}}

\makeatletter

\let\itemLaTeX=\item
% Have to use a \newcount because (ugh) the LaTeX \setcounter is a global assignment
\newcount\itemsSoFar
\newcommand\onlyitemsItem[1][]{%
 % The \egroup closes off any \vbox'es from the previously ignored \item.
 % The conditional \bgroup cancels it out when there aren't any.
 \itemIf\bgroup\fi\egroup
 \let\itemIf=\iffalse%\fi
 \advance\itemsSoFar by 1 %
 \onlyitemsset{utility/store tag = {}, utility/verdict/.style = {}, select/\the\itemsSoFar, #1, utility/verdict}%
 \itemIf
  \def\next{\expandafter\itemLaTeX\itemOptions}%
 \else
  % The vbox is set and then discarded, effectively ignoring an entire \item.
  % This inner \itemLaTeX is necessary to avoid a glitch when we
  % ignore the first \item of an itemize that itself contains a nested
  % \itemize.  Don't ask, I don't know.
%
%the \@gobble s are so that various numerated lists still work as if
%the items are missing (i.e, no side effects). reference: http://tex.stackexchange.com/questions/34811/how-to-skip-an-item-entirely-with-no-side-effects/34821#34821
  \def\next{\setbox0=\vbox\bgroup\let\stepcounter=\@gobble\let\addtocounter\@gobbletwo\itemLaTeX}%
 \fi
 \next
}
\makeatother

\newcommand\onlyitems[2][]{%
\let\item=\onlyitemsItem
 \let\itemIf=\iftrue
 \itemsSoFar = 0 %
 % We have to reset here so that the selections from an outer itemize don't conflict with an inner one.
 \onlyitemsset{reset, #1}%
 #2%
 % This closes off the last \vbox
 \itemIf\bgroup\fi\egroup
}
12
  • 3
    Why don’t you keep those lists in BibTeX, or a references managing system of your choice?
    – Caramdir
    Oct 24, 2010 at 15:35
  • hmmm. interesting idea. I'll give it a go. I'm not sure I can do everything with BibTex, but I haven't tried. Oct 24, 2010 at 15:58
  • The bibentry package is probably useful here. See also tex.stackexchange.com/questions/222/…
    – Caramdir
    Oct 24, 2010 at 16:38
  • @caramdir: I'm trying again, to no avail...would you mind posting a small example how one could do this? Oct 11, 2011 at 22:00
  • Started a bounty as I'm not satisfied with the given answers.... Oct 24, 2011 at 14:59

5 Answers 5

25

Here is another answer, now complete. It has the following features:

  1. It uses pgfkeys.

  2. It is almost completely syntax-compatible with the normal {itemize} environment. The only change is that I had to make \item take key-value optional arguments, so that what used to be the sole optional argument is now called tag=<something>; this causes <something> to be the "bullet" for the item, as before.

    Edit: At Yossi's suggestion, I've replaced the modified environment with a "filter" command \onlyitems that you can use like this: \begin{environment}\onlyitems[inclusion options]\ItemListMacro.... (If you don't use a macro, of course the items must go in braces.)

  3. You pass key-value options to \begin{itemize} specifying your selection. You can say either include = {<list>} or exclude = {<list>}, where <list> is a comma-separated list of numbers and text. The numbers can be given in ranges a,...,b like in \foreach. The text can be anything that pgfkeys thinks is a good name for a key. You can also say all or none; the default is all, so with no options at all, this behaves just like itemize usually does.

  4. The order of these options is somewhat important: alternating include and exclude specifications will do what you expect, unless you expect id's and numbers to be treated equally. If you ask both to include and exclude an \item that happens to match both a number and an id, the id wins. The all and none options are "weak": they can be given anywhere, do not interact with the others, and are overridden by any explicit specification.

  5. Each \item takes an optional argument that can have the keys tag = <bullet> as described above, or id = <label>, where <label> is something you might include or exclude. The \items are also secretly enumerated as they go (this enumeration does not depend on what is included; i.e. it depends only on how many \items are in the TeX file itself), and each one is matched against the selection criteria first by number, and then by id. You can even give multiple id's, or use the same id for multiple items, with the expected behavior.

  6. The exclusion of items is compatible with nested itemize environments. Selection criteria do not interact; enumerations do not interact. Anything whatsoever can be in an \item and this thing will still work. I would have had this up six hours ago if it weren't for that.

And here it is:

\documentclass{article}
\usepackage{pgfkeys,pgffor}

% A huge list of pgfkeys to be set up.  
\pgfkeys{
 /onlyitems/.is family, /onlyitems,
 % Utility keys
 utility/store tag/.store in = \itemOptions,
 utility/store bool/.store in = \itemIf,
 utility/process true/.style = {process/#1 = \iftrue},
 utility/process false/.style = {process/#1 = \iffalse},%\fi
 utility/set store bool/.style 2 args = {#1/.style = {utility/store bool = #2}},
 utility/set select style/.style 2 args = {utility/set store bool = {select/#1}{#2}},
 utility/verdict/.style = {},
 utility/add to reset/.style = {
  reset/.prefix style = {select/#1/.style = {select/.unknown}}
 },
 utility/current key/.estore in = \itemKey,
 % Nothing in the process family is ever set, so any selection option is set to store a boolean
 process/.unknown/.style = {
  utility/current key = \pgfkeyscurrentname,
  select/\itemKey/.style = {
   utility/set store bool = {utility/verdict}{#1}
  },
  utility/add to reset/.expand once = \itemKey
 },
 % For clearing the list of selected items (to support nested {itemize})
 reset/.style = {},
 reset/reset/.style = {reset/.style = {reset/reset, default}},
 reset/reset,
 % Here are the options you can actually pass.  These go to the {itemize}...
 include/.style = {utility/process true/.list  = {#1}},
 exclude/.style = {utility/process false/.list = {#1}},
 all/.style  = {utility/set select style = {.unknown}{\iftrue}},
 none/.style = {utility/set select style = {.unknown}{\iffalse}},%\fi
 default/.style = {all},
 % ...and these to the \item's
 tag/.style = {utility/store tag = {[#1]}},
 id/.style = {select/#1},
}

% Like tikzset, it sets a default key path
\newcommand\onlyitemsset[1]{\pgfkeys{/onlyitems, #1}}

\let\itemLaTeX=\item
% Have to use a \newcount because (ugh) the LaTeX \setcounter is a global assignment
\newcount\itemsSoFar
\renewcommand\item[1][]{%
 % The \egroup closes off any \vbox'es from the previously ignored \item.
 % The conditional \bgroup cancels it out when there aren't any.
 \itemIf\bgroup\fi\egroup
 \let\itemIf=\iffalse%\fi
 \advance\itemsSoFar by 1 %
 \onlyitemsset{utility/store tag = {}, utility/verdict/.style = {}, select/\the\itemsSoFar, #1, utility/verdict}%
 \itemIf
  \def\next{\expandafter\itemLaTeX\itemOptions}%
 \else
  % The vbox is set and then discarded, effectively ignoring an entire \item.
  % This inner \itemLaTeX is necessary to avoid a glitch when we ignore the first \item of an itemize that itself contains a nested \itemize.  Don't ask, I don't know.
  \def\next{\setbox0=\vbox\bgroup\itemLaTeX}%
 \fi
 \next
}

% \let\itemizeLaTeX=\itemize
% \let\enditemizeLaTeX=\enditemize
% \renewcommand\itemize[1][]{%
%  \let\itemIf=\iftrue
%  \itemsSoFar = 0 %
%  % We have to reset here so that the selections from an outer itemize don't conflict with an inner one.
%  \onlyitemsset{reset, #1}%
%  \itemizeLaTeX
% }
% \renewcommand\enditemize{%
%  % This closes off the last \vbox
%  \itemIf\bgroup\fi\egroup\enditemizeLaTeX
% }

\newcommand\onlyitems[2][]{%
 \let\itemIf=\iftrue
 \itemsSoFar = 0 %
 % We have to reset here so that the selections from an outer itemize don't conflict with an inner one.
 \onlyitemsset{reset, #1}%
 #2%
 % This closes off the last \vbox
 \itemIf\bgroup\fi\egroup
}

\begin{document}

\newcommand{\mylist}{
 \item First item
 \item[id=banana] Second item, id=banana
 \item Third item, with a nested itemize
  \begin{itemize}\onlyitems
   \item Nested one
   \item Nested two
   \item Nested three
  \end{itemize}
 \item[id=banana, id=papaya] Fourth item, id=banana and id=papaya
}

\noindent
All:
\begin{itemize}\onlyitems
  \mylist
\end{itemize}

\noindent
Excluding 1,2,3.  Note that there is no interference with the inner itemize.
\begin{itemize}\onlyitems[exclude = {1,2,3}]
 \mylist
\end{itemize}

\noindent
With 2,\dots,4 excluded; again no interference.
\begin{itemize}\onlyitems[exclude = {2,...,4}]
 \mylist
\end{itemize}

\noindent
With id=banana excluded:
\begin{itemize}\onlyitems[exclude = banana]
 \mylist
\end{itemize}

\noindent
With 1, id=papaya excluded:
\begin{itemize}\onlyitems[exclude = {1, papaya}]
 \mylist
\end{itemize}

\noindent
With none, then id=banana included, then 4 excluded.  Note that the label has precedence over the number.
\begin{itemize}\onlyitems[none, include = banana, exclude = 4]
 \mylist
\end{itemize}

\end{document}

enter image description here

The non-pgfkeys part is really sort of straightforward: the keys somehow determine a boolean \itemIf that, in turn, switches between placing a real \item (saved as \itemLaTeX) and saving the next "\item" in a \vbox and throwing it away. There's some trickery with braces to make that work, but it's nothing special.

The pgfkeys block is pretty elaborate, though. The basic principle of its operation is:

  • I want, ultimately, for each \item to process its number (saved in \itemsSoFar) and its id as keys, and these keys should have been set up previously (in the options to \begin{itemize}) to appropriately switch \itemIf. Correspondingly, I need that numbers and id's that were not set to be handled according (via a .unknown handler) to the default action (all or none, basically). This part is contained in the select key subtree.

  • The way that \begin{itemize} sets up these keys is by means of another .unknown handler. This one is contained in the process key subtree, and is in fact the sole inhabitant of that subtree: any key that gets called in that path is unknown and subject to this handler. This handler defines keys in the select key subtree, so process remains vacant. This makes it possible to put the same id or number in include and exclude specifications repeatedly.

  • However, I can't just have each id or number key directly alter \itemIf, because I need them to override each other and also the unknown id's and numbers. It wouldn't do if, for example, I wrote \item[id = label] in the third item, and it happened that 3 were included and label were unknown but processed second, so that in fact the item would be excluded despite its only specified characteristic being explicitly included. So I need to delay the decision until all the characteristics are processed.

  • The way this delay is implemented is by having each id and number key put its vote into a verdict key that is executed last. The .unknown handled keys still modify \itemIf directly, but then verdict, if it was set, does whatever was last put in it, overruling the default if any explicit choice was triggered.

  • In order to support nested itemize environments, I need to be able to unset the selection criteria. Alas, pgfkeys has no way of unsetting keys. Fortunately, I don't need that: I just need that any keys that were previously set be redefined to act just like the .unknown handler (this is basically unsetting the key, actually). So in addition to telling a newly-defined id or number key to set up the verdict key, the process/.unknown handler also attaches a piece of code to the reset key that tells it to undo the other thing. Then reset is called at the beginning of every itemize.

  • The whole operation is basically a chain of .style handlers. I never store a value (okay, I do it once to expand \pgfkeyscurrentname) and I never set a .code directly. Every key, when executed, just expands to a few more keys, the end result of which is (finally) to set \itemIf. If you didn't know already, pgfkeys is not so much a key-value package as an elaborate finite-state machine specification language that also happens to contain a much better reimplementation of TeX's macro language than LaTeX. The fact that it has a key-value syntax is pretty much incidental.

8
  • Hmmm. using pgf...Will this work in the LaTeX->dvi->ps workflow? Oct 25, 2011 at 21:01
  • @Yossi: The sample above works perfectly. The subset of PGF I'm using is just a macro language and doesn't actually use the "low-level" stuff, so it should be format-independent.
    – Ryan Reich
    Oct 25, 2011 at 21:06
  • 3
    I don't actually need the onlyitem stuff; however this piece of code really is worth studying. One can learn quite a lot from it. Thanks Ryan for your elaborative and inspirational answer.
    – Daniel
    Oct 27, 2011 at 7:28
  • @Daniel: Thanks, I really appreciate that. I hope you do learn from it; it's taken me quite a while to understand what it represents about pgfkeys.
    – Ryan Reich
    Oct 27, 2011 at 15:45
  • @Yossi: Right, I should have mentioned that this is itemize-specific but only superficially. If you reprogram another list environment in exactly the same way as I did itemize, it should work fine.
    – Ryan Reich
    Nov 9, 2011 at 0:15
8

Here is a solution:

\documentclass[a4paper]{article}

\makeatletter
\newcommand{\itemlist}[2]{\@namedef{itemlist@#1}{#2}}
\newcommand{\useitemlist}[2][]{%
  \if\relax\detokenize{#1}\relax
    \let\do\noitemlistcheck
  \else
    \@for\next:=#1\do
      {\expandafter\let\csname useitemlist@\next\endcsname\@empty}
    \let\do\itemlistcheck
  \fi
  \@nameuse{itemlist@#2}%
  }
\newcommand{\noitemlistcheck}[1]{\item}
\newcommand{\itemlistcheck}[1]{%
  \ifcsname useitemlist@#1\endcsname
    \expandafter\item
  \else
    \expandafter\@gobbleopt
  \fi}
\newcommand\@gobbleopt[2][]{}

\makeatother

\begin{document}
\itemlist{mylist}{%
  \do{a}[$\star$]{first item}
  \do{b}{second item}
  \do{c}{third item}
}

The following list will show all items:
\begin{itemize}
\useitemlist{mylist}
\end{itemize}

The following list will show only items "a" and "c"
\begin{itemize}
\useitemlist[a,c]{mylist}
\end{itemize}

\end{document}

You define the list of items with

\itemlist{<name>}{%
   \do{<label-1>}{<item text>}
   \do{<label-2>}{<item text>}
   ...
   \do{<label-n>}{<item text>}
}

and then say \useitemlist{<name>} for printing the entire list or \useitemlist[<list of labels>]{<name>} for printing only the desired items calling them by the symbolic name given as first argument.

In the list I've shown that \do, after the key, can take an optional argument that becomes that of \item.

2
  • how would you program \do to accept an optional label, and not need \begingroup, \endgroup surrounding the item. in other words, could you make it work more like a regular \item? Oct 24, 2011 at 18:18
  • @YossiFarjoun The \begingroup-\endgroup pair is not necessary. I'll edit the answer to have also an optional argument for \item.
    – egreg
    Oct 24, 2011 at 19:21
7

If you really want to, you can abuse biblatex+biber to create any lists. The following is extremely simplistic. I guess one could write a configuration files for biber to make things look more natural (and have sorting if desired).

Anyway, biblatex defines the custom[a-f] entry type which can (amongst others) contain the user[a-f] fields. So one could create .bib file like this.

@customa{entry-one,
    usera = {one}
}
@customa{entry-two,
    usera = {two \textit{bla} bla\par
    bla}
}
@customa{entry-three,
    usera = {three}
}
@customa{entry-four,
    usera = {four}
}
@customa{entry-five,
    usera = {five}
}
@customa{entry-six,
    usera = {six}
}

Then it only remains to tell biblatex how to format things and use \nocite to tell it which part of the list to print:

\documentclass{article}
\usepackage[backend=biber]{biblatex}

% Make `\printbibliography[env=list]` print a simple itemize list
\defbibenvironment{list}
    {\begin{itemize}}
    {\end{itemize}}
    {\item}
% Define how `customa` entries are displayed (simply pass through the usera field).
\DeclareBibliographyDriver{customa}
    {\printfield{usera}}

\addbibresource{test.bib}

\begin{document} 
\nocite{entry-one,entry-six,entry-four,entry-two}

\printbibliography[env=list,heading=none]
\end{document}

Running

pdflatex test
biber test
pdflatex test

then produces

result

One warning: biber seems to remove newlines from the usera field. This means that paragraphs have to be explicitly marked with \par and that there cannot be any comments.

If you want several of these lists in one document, the refsection environment (see the biblatex manual) can used. If you also want a normal bibliography, you can use biblatex's facilities to handle several separate bibliography resources.

2
  • Nice. Thanks. I still haven't migrated to bibletex...it still seems a little odd to make lists like this with a bibliography tool...but hey, it works. Oct 27, 2011 at 14:34
  • Nice to see an example of biblatex. I just use amsrefs but I keep hearing good things about this.
    – Ryan Reich
    Oct 27, 2011 at 18:45
6
+200

Note: This answer was given the bounty by mistake, or possibly extraterrestrial interference. Please read my other answer.

I had this problem when trying to maintain three versions of my CV, each with different levels of detail. In a nutshell, what I came up with is sort of a hack involving the comment environment from the verbatim package. Here is what I wrote:

% These lists have optional arguments which indicate the formats in which
% they are to be displayed.  To display always, write [*], which is the default.

\newif\ifdisplaythis\displaythistrue
\newif\ifinner

\newsavebox{\casket}
\newcommand{\behead}[1]{\basketmacro #1}
\newcommand{\basketmacro}[1]{\gdef\basket{#1}}

\newcommand{\checkresumeformat}[1]{
 \ifthenelse{\equal{*}{#1}}%
  {\displaythistrue\listbreak}{%
   \savebox{\casket}{\behead{#1}}% Hackish way of removing the evidence
   \ifthenelse{\equal{\basket}{-}}%
    {\ifthenelse{\equal{-\resformat}{#1}}{}{\displaythistrue}\listbreak}%
    {\ifthenelse{\equal{\resformat}{#1}}{\displaythistrue\listbreak}{}}%
  }%
}

\newcommand{\testifallowed}[1]{%
 \displaythisfalse%
 \let\do = \checkresumeformat%
 \docsvlist{#1}%
}

% Logic:
% \reslist and \resitem both set \innerfalse and then test for being commented.
% When \endreslist or \endresitem comes around, we need to know whether to end
% the comment or not.
% Each of them decides this and then sets \innertrue.
% The decision is: if \ifinner is true, then there must have been a nested 
% environment, so we were
% not commented.  Otherwise, we can check \ifdisplaythis, which can't have been 
% changed by anything
% since there was nothing to change it.

\def\maybecomment#1#2{%
 \testifallowed{#1}%
 \ifthenelse{\boolean{displaythis}}%
  {#2}%
  {\comment}%
}

\def\endmaybecomment#1{%
 \ifthenelse{\boolean{inner} \OR \boolean{displaythis}}%
  {#1}%
  {\endcomment}%
}

\newenvironment{reslist}[1][*]
 {\innerfalse\maybecomment{#1}{\compactitem}}
 {\endmaybecomment{\endcompactitem}\innertrue}

% resitem...
\newenvironment{resitem}[1][*]
 {\innerfalse\maybecomment{#1}{\item}}
 {\endmaybecomment{}\innertrue}

You'll want to load etoolbox and ifthen to make this work (I also use paralist for the compact lists, but you can just replace that with the usual itemize). You may also want to ignore the business with \ifinner, unless you plan on having nested lists (but see below).

The idea is that somewhere, the macro \resformat is set (I wrote this as a class file, so it is set in an option, but you can define it manually). Let's say it's set to CV, but you also have a resume format. Then you might write a list like:

\begin{reslist}[*]
 % This list is displayed in both the CV and resume
 \begin{resitem}[CV]
  This goes in the CV only.
 \end{resitem}
 \begin{resitem}[resume]
  This goes in the resume only.
 \end{resitem}
 \begin{resitem}[CV,resume]
  This is displayed in both the CV and resume.
 \end{resitem}
 \begin{resitem}[-CV]
  This is displayed in every format other than CV.
\end{reslist}

This is not unlike how beamer works, if memory serves.

Now, suppose you have a nested list; that is, something like:

\begin{reslist}
 \begin{resitem}[*]
  This item is always displayed.
 \end{resitem}
 \begin{resitem}[resume]
  This item is only in the resume, and has another list.
  \begin{reslist}
   \item If you don't need the switches, you can use normal notation for lists.
  \end{reslist}
 \end{resitem}
\end{reslist}

My code is designed to handle this. However, the verbatim code (whence the commenting functionality comes) is not. Thus, you must modify it to recognize nested comments (or nested verbatims, more generally. See the docs for why this is the same thing). I did this, though I am currently reluctant to put it online since I am not sure of the legalities so I feel like I should keep it to myself (for now). If you know what incantations I should say to make it okay to distribute, tell me and I'll do it.

You may be wondering why I chose to use the (awkward) notation in which each item is an environment. This is a necessary evil coming from the way the code in verbatim is designed: the pair \comment and \endcomment which delimits the commenting environment can tell from which environment it is called, but not from which scope. That is, it has to be called from an environment so that it can find the \end{environmentname} tag (which does not have to be \end{comment}; for me, it is \end{reslist} or \end{resitem}). One can not "comment everything between the braces". It also does not work if that tag is not visible in the unprocessed source code, so no tricks like

\let\olditem = \item
\def\item{\endcomment\olditem\comment}
\newenvironment{reslist}{\begin{itemize}\comment}{\endcomment\end{itemize}}

since \comment will not expand the \item's to figure out whether there is an \endcomment embedded. I know how it is possible to modify \comment so as to get around this, but I haven't done it because it was enough work to get the nested comments working (for now).

Perhaps you will find this helpful.

1
  • :-( I meant to mark your other answer with the bounty. I don't know what happened. Oct 31, 2011 at 18:47
5

Here's my approach. Note that it requires eTeX and the etoolbox package.

\documentclass{article}

\usepackage{etoolbox}

\makeatletter

\newenvironment{DefConditionalList}[1]
  {\def\citem##1##2{%
    \ifcsname ConditionalList@#1@item@##1\endcsname
      \PackageError{ConditionalList}%
                   {List item `##1' in conditional list `#1' already defined}%
                   {The list item `##1' was already defined via
                    `\noexpand\citem{##1}{...}' in the conditional list `#1'.}%
    \else
      \csgdef{ConditionalList@#1@item@##1}{\item ##2}%
      \csgdef{ConditionalList@#1@item@##1 }{\item ##2}%
    \fi}}
  {}

\newcommand*{\ConditionalList}[3][]{\begingroup
  \ifblank{#3}{%
    #1%
  }{%
    \def\do##1{%
      \ifcsname ConditionalList@#2@item@##1\endcsname
        \csname ConditionalList@#2@item@##1\endcsname
      \else
        \PackageError{ConditionalList}%
                     {Unknown list item `##1' in conditional list `#2'}%
                     {The list item `##1' was not defined via
                      `\noexpand\citem{##1}{...}' in the conditional list
                      `#2'.}%
      \fi
    }%
    \docsvlist{#3}%
    \let\ConditionalList@DisplayItem\ConditionalList@DisplayItem@error
  }%
\endgroup}

\makeatother

\begin{DefConditionalList}{MyList}
  \citem{first}{Alpha.}
  \citem{second}{Beta.}
  \citem{third}{Gamma.}
  \citem{fourth}{Delta.}
\end{DefConditionalList}

\edef\resume{first, fourth}

\begin{document}
  \begin{itemize}
    \ConditionalList[\item Nothing specified!]{MyList}{first, third}
    \ConditionalList[\item Nothing specified!]{MyList}{ fourth
                                                      , third
                                                      , fourth }
  \end{itemize}

  \begin{enumerate}
    \ConditionalList[\item Nothing specified!]{MyList}{}
    % Eror: \ConditionalList[\item Nothing specified!]{MyList}{nonexistent}
  \end{enumerate}
\end{document}

This produces the following output:

  1. Alpha.
  2. Gamma.
  3. Delta.
  4. Gamma.
  5. Delta.
  • Nothing specified!

The idea is to define package-private commands for every item specified in a DefConditionalList environment. Then, \ConditionalList can loop over the requested names, check to see if their corresponding commands are defined, and insert them if so. Order and repetition are irrelevant, you get an error if a non-existent key is specified, and you can specify something to do if no items are specified (by default, nothing is inserted). Defining conditional list items with spaces at the end is not permitted (though it might work anyway…), so that arbitrary spaces can be provided around the commas.

You must log in to answer this question.

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