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}
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}
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> }