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.

I have created a macro, \joinlist, that adds commas or other delimiters between the elements of an etoolbox internal list. It has special handling for lists of just two elements ("a and b"). It also knows how to put a different delimiter between the last two items of longer lists ("a, b, and c"). Here is the definition, created by me:

% typical use: \joinlist{\listmacro}{, }{, and }{ and }

\RequirePackage{etoolbox}

\newcommand{\@join@ignore}[1]{} % used to ignore current list element when counting
\newcount\@join@listlength % used to count length of list
\newcount\@join@currentnum % used to track current list element number

\newcommand{\joinlist}[4]{%
  %
  % count list elements
  \@join@listlength 0 %
  \forlistloop{\advance\@join@listlength 1\relax\@join@ignore}{#1}%
  %
  % now join list elements, tracking current element number
  \@join@currentnum 0 %
  \forlistloop{%
    \advance\@join@currentnum 1 %
    \ifnumequal{\the\@join@currentnum}{1}%
    {}% first
    {% not first
      \ifnumequal{\the\@join@currentnum}{\the\@join@listlength}%
      {% last
        \ifnumequal{\the\@join@listlength}{2}%
        {#4}% last of exactly two
        {#3}% last of more than two
      }%
      {#2}% neither first nor last
    }%
  }%
  {#1}}

Save the above as join-list.sty, then consider the following small example showing a problem:

\documentclass{article}

\usepackage{hyperref}
\usepackage{join-list}

\begin{document}

    \newcommand{\people}[0]{}
    \listadd{\people}{a}
    \joinlist{\people}{, }{, and }{ and }  % a

    \listadd{\people}{b}
    \joinlist{\people}{, }{, and }{ and }  % a and b

    \listadd{\people}{c}
    \joinlist{\people}{, }{, and }{ and }  % a, b, and c

    \hypersetup{pdfauthor=\joinlist{\people}{, }{, and }{ and }}

\end{document}

\joinlist works fine for creating document body content. We see "a", then "a and b", then "a, b, and c" as expected.

Unfortunately, using this in \hypersetup{pdfauthor=...} does not work as hoped. Instead, it sets the document's PDF author metadata to "0 1110 1 , and a1 , and b1 , and c". Clearly something has gone badly wrong with expanding/evaluating the \joinlist macro in this context.

What am I doing wrong here? How can I fix this? Are there other ways that my \joinlist macro could be improved? Does a similar macro already exist in some widely-available package? (I searched but found none.)

share|improve this question

1 Answer 1

up vote 5 down vote accepted

The problem is that \joinlist is not "expandable": it must perform assignments such as \advance\@join@currentnum 1 that can't be done in the value of pdfauthor=.

With expl3 (the not any more so experimental programming layer for LaTeX3), there is what you need.

\documentclass{article}

\usepackage{hyperref}

\RequirePackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\joinlist}{ m m m m }
 {
  \seq_use:cnnn { g_liblit_list_#1_seq } { #2 } { #3 } { #4 }
 }
\cs_generate_variant:Nn \seq_use:Nnnn { c }
\NewDocumentCommand{\newlist}{ m }
 {
  \seq_new:c { g_liblit_list_#1_seq }
 }
\NewDocumentCommand{\listadd}{ m m }
 {
  \seq_gput_right:cn { g_liblit_list_#1_seq } { #2 }
 }
\ExplSyntaxOff

\begin{document}

\newlist{people}
\listadd{people}{a}
\joinlist{people}{ and }{, }{, and } % a

\listadd{people}{b}
\joinlist{people}{ and }{, }{, and }  % a and b

\listadd{people}{c}
\joinlist{people}{ and }{, }{, and }  % a, b, and c

\hypersetup{pdfauthor=\joinlist{people}{ and }{, }{, and }}

\end{document}

There are some changes with respect to your setting. Lists have a name, rather than be called by a macro; a new one can be created by \newlist. Also the order of the arguments to \joinlist is different:

  1. the list name;
  2. what should go between the elements in case of two elements;
  3. what should go between the elements when there are more than two (except the last two);
  4. what should go between the last two elements when there are more than two.

In case the list is empty nothing will be produced; with only one element, that one will be produced.

This version of \joinlist is expandable and there's no problem in giving it as a value to pdfauthor=.

You can use the code in the preamble as a drop replacement of yours in the .sty file.

enter image description here

share|improve this answer
    
Since you reference the original problem being the use of assignments (causing the non-expandability), yours does not use any assignments, correct? –  Werner Apr 3 '13 at 21:41
    
@Werner Courtesy of Bruno Le Floch (and the LaTeX3 team), \seq_use:Nnnn works by pure expansion. –  egreg Apr 3 '13 at 21:42
    
This looks encouraging! But I am seeing an error: Control sequence \seq_use:Nnnn undefined. Perhaps my version of some supporting code is out-of-date? I am using expl3 version 2729, dated 2011/09/05, from TeXLive 2011. –  Ben Liblit Apr 3 '13 at 22:05
    
@BenLiblit Sorry, but this is available only with an up-to-date TeX distribution. And TeX Live 2011 is surely obsolete: in a few weeks the test version of the 2013 release will be available. –  egreg Apr 3 '13 at 22:18
    
Confirmed as working under TeXLive 2012. Looks like it's time to ask my system administrators to update our installation! –  Ben Liblit Apr 3 '13 at 22:27

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.