1

I have a document containing many instances of an environment with a mandatory argument providing a short "tag." These tags are something like "M04", and I've written a macro using xstring which convert this to, e.g., "May!2004". I'd like to use this macro to automatically add an index entry using imakeidx.

It seems there are problems at various stages stemming from macro expansion. In the MWE below, I can use \index{\genkey{M04}} to add to the index, but it seems that \genkey is expanded too late, because "May!2004" appears literally in the index (rather than with 2004 as a sub-entry). I can't get even to this point with the environment - I end up with many complaints about undefined control sequences coming from the xstring package.

I've attempted to use edef to get all expansion done with before passing to \index, but to no avail - as per this answer, it is impossible to use edef in conjunction with string.

Any help on how to get the code below to work as desired would be much appreciated.

\documentclass{article}
\usepackage{xstring}
\usepackage{imakeidx}

\makeindex

\newcommand\getmonth[1]{%
  \StrChar{#1}{1}[\firstchar]%
  \IfStrEq{\firstchar}{J}{January}{May}%
}
\newcommand\getyearprefix[1]{%
  \StrChar{#1}{2}[\secondchar]%
  \IfStrEq{\secondchar}{9}{19}{20}%
}
\newcommand{\genkey}[1]{%
    \getmonth{#1}!\getyearprefix{#1}\StrMid{#1}{2}{3}%
}

\newenvironment{entry}[1]{%
  \index{\genkey{#1}}%
}
{}

\begin{document}

\genkey{M04}

\genkey{J98}

\index{\genkey{M04}}

% \begin{entry}{M04}
% Here is the M04 entry
% \end{entry}

% \begin{entry}{J98}
% Here is the J98 entry
% \end{entry}

\printindex

\end{document}
  • Are the keys guaranteed to be one letter followed by two numbers, or might this change as well? – Skillmon likes topanswers.xyz Sep 26 '19 at 6:08
  • And what's with the months that have the same initial letter? Is M always May, or could it be March? – Skillmon likes topanswers.xyz Sep 26 '19 at 6:12
2

The following defines expandable versions of \getmonth, \getyearprefix and \genkey. The argument must consist of 3 characters (the argument is detokenized).

\documentclass{article}

\usepackage{imakeidx}
\makeindex

\usepackage{xparse}

\ExplSyntaxOn
\NewExpandableDocumentCommand \getmonth { +m }
  {
    \str_case:nn { #1 }
      {
        { J } { January }
        { F } { February }
        { m } { March }
        { a } { April }
        { M } { May }
        { j } { June }
        { J } { July }
        { A } { August }
        { S } { September }
        { O } { October }
        { N } { November }
        { D } { December }
      }
  }
\NewExpandableDocumentCommand \getyearprefix { +m }
  {
    \int_case:nn { #1 }
      {
        { 0 } { 20 }
        { 1 } { 20 }
        { 2 } { 19 }
        { 3 } { 19 }
        { 4 } { 19 }
        { 5 } { 19 }
        { 6 } { 19 }
        { 7 } { 19 }
        { 8 } { 19 }
        { 9 } { 19 }
      }
  }
\msg_new:nnn { genkey } { improper-length }
  { The ~ token ~ list ~ has ~ the ~ wrong ~ length ~ ( #1 ) }
\cs_new:Npn \__genkey:nnn #1 #2 #3
  {
    \getmonth { #1 } ! \getyearprefix { #2 } #2 #3
  }
\NewExpandableDocumentCommand \genkey { +m }
  {
    \int_compare:nNnTF { \str_count_ignore_spaces:n { #1 } } = 3
      { \exp_after:wN \__genkey:nnn \tl_to_str:n { #1 } }
      {
        \msg_expandable_error:nnf { genkey } { improper-length }
          { \str_count_ignore_spaces:n { #1 } }
      }
  }
\NewDocumentEnvironment { entry } { m }
  {
    \exp_args:Ne \index { \genkey { #1 } }
  }
  {}
\ExplSyntaxOff

\begin{document}

\genkey{M04}

\genkey{J98}

% doesn't work, honestly no idea why index doesn't always expand its argument
\index{\genkey{M04}}

\begin{entry}{M04}
Here is the M04 entry
\end{entry}

\begin{entry}{J98}
Here is the J98 entry
\end{entry}

\printindex

\end{document}
| improve this answer | |
  • Poor July, will always be shadowed by the mighty January ;-) – Phelype Oleinik Sep 26 '19 at 11:35
  • July will never be selected. – egreg Sep 26 '19 at 12:51
  • Beautiful! This will motivate me to finally learn expl3. If I understand correctly, this works because unlike xstring, the \getmonth and related macros don't make assignments and are therefore expandable? – srossd Sep 26 '19 at 13:46
  • Oh, forgot about the January clash, my bad. Suggestions? – Skillmon likes topanswers.xyz Sep 26 '19 at 14:48
  • @srossd correct, in interface3.pdf fully expandable macros are marked so you can see what can be declared expandable and what not. – Skillmon likes topanswers.xyz Sep 26 '19 at 14:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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