choose from a list with xstring

The solution with xstring and eTeX's \numexpr:

\documentclass{article}

\usepackage{xstring}

\newcommand*{\variation}[2]{%
  \StrBetween[{#1},{\numexpr(#1)+1\relax}]{;#2;}{;}{;}%
}

\newcommand*{\test}[1]{%
  \detokenize{#1} $\rightarrow$ #1%
}

\begin{document}
\noindent
\ttfamily
\test{\variation{1}{aaa;bbb;ccc}}\\
\test{\variation{2}{aaa;bbb;ccc}}\\
\test{\variation{3}{aaa;bbb;ccc}}
\end{document}

Result

Update

  • \StrBetween knows a final optional argument, that allows to store the result in a macro. Because I did not know, how \variation is used, the macro inherited the final optional argument. The usual looking for optional arguments in LaTeX (based on \@ifnextchar) removes following white spaces to find the opening square bracket. \relax stops the looking for [ and the space after the argument braces of \variation is kept.

  • Second problem from the comments: Package xstring adds spaces (uncommented line ends) after numerical comparisons. In case of explicit numbers, the space is ignored and the parsing of the number finished. But if the number is given by an integer register or in this case by a complete \numexpr, the space is not needed and kept.

    There are several ways to cure this:

    • Bug report.
    • \the\numexpr instead of \numexpr
    • Expanding the arguments and convert to explicit numbers before. Then \StrBetween only sees the expanded numbers.

New test file:

\documentclass{article}

\usepackage{xstring}

\newcommand*{\variation}[2]{%
  \edef\variationNext{%
    \noexpand\StrBetween[%
      \the\numexpr(#1)\relax,%
      \the\numexpr(#1)+1\relax
    ]%
  }%
  \variationNext{;#2;}{;}{;}\relax
}

\newcommand*{\test}[1]{%
  \detokenize\expandafter{\string#1} $\rightarrow$ [#1]%
}

\begin{document}
\noindent
\ttfamily
\test{\variation{1}{aaa;bbb;ccc}}\\
\test{\variation{2}{aaa;bbb;ccc}}\\
\test{\variation{3}{aaa;bbb;ccc}}
\end{document}

Result


Here's an option using xparse/expl3 rather than xstring (if you're only interested in an xstring solution, then just take this as comparison). The variation macro takes an optional first argument that determines the character to split the list at, the default is ;.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\msg_new:nnn {variation}{index~out~of~bounds}{You~have~requested~an~item~that~doesn't~exist}

\NewDocumentCommand{\variation}{ O{;} m m }
 {
    % split arg #3 at every occurrence of #1 (default #1=;) and store in sequence
    \seq_set_split:Nnn \l_tmpa_seq {#1}{#3}

    % compare arg #2 to the length of the sequence
    \int_compare:nTF {#2>\seq_count:N \l_tmpa_seq}

        % if arg #2 > sequence length, then the item doesn't exist
        {\msg_warning:nnn {variation}{index~out~of~bounds}{}}

        % if arg #2 <= sequence length, then return requested item.
        {\seq_item:Nn \l_tmpa_seq {#2}}
 }

\ExplSyntaxOff

\begin{document}

\variation{3}{aaa;bbb;ccc;ddd;eee}
\variation{1}{aaa}
\variation{2}{aaa}

\end{document}

Just a variation on Scott H.'s solution, for showing a perhaps better way to deal with the problem.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\variation}{ s O{;} m m }
 {
  \IfBooleanTF{#1}
   {
    \geoff_variation_check:nnn { #2 } { #3 } { #4 }
   }
   {
    \geoff_variation_nocheck:nnn { #2 } { #3 } { #4 }
   }
 }

\msg_new:nnn { variation }
 { index~out~of~bounds }
 { You~have~requested~an~item~that~doesn't~exist }

\seq_new:N \l__geoff_item_list_seq

\cs_new_protected:Npn \geoff_variation_nocheck:nnn #1 #2 #3
 {
  \seq_set_split:Nnn \l__geoff_item_list_seq { #1 } { #3 }
  \seq_item:Nn \l__geoff_item_list_seq { #2 }
 }

\cs_new_protected:Npn \geoff_variation_check:nnn #1 #2 #3
 {
  \seq_set_split:Nnn \l__geoff_item_list_seq { #1 } { #3 }
  \int_compare:nTF { #2 > \seq_count:N \l__geoff_item_list_seq }
   { \msg_warning:nn { variation }{ index~out~of~bounds } }
   { \seq_item:Nn \l__geoff_item_list_seq {#2} }
 }

\ExplSyntaxOff

\begin{document}

\variation{3}{aaa;bbb;ccc;ddd;eee}

\variation[,]{3}{aaa,bbb,ccc,ddd,eee}

\variation{1}{aaa}

\variation{2}{aaa}

\variation*{2}{aaa}

\end{document}

The \NewDocumentCommand instruction should be used to pass the arguments to an "internal" function. In order to better show how, I introduce a *-variant, so the control is passed either to \geoff_variation_check:nnn or to \geoff_variation_nocheck:nnn. The internal functions should be protected, since they do unexpandable actions (\set...).

The bulk of the action is splitting the argument into a sequence and returning the needed sequence item:

\seq_set_split:Nnn <sequence> { <tokens> } { <token list> }

\seq_item:Nn <sequence> { <integer expression> }

Tags:

Xstring