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 want a command that does the following:

\variation{1}{aaa;bbb;ccc} ----> aaa
\variation{2}{aaa;bbb;ccc} ----> bbb
\variation{3}{aaa;bbb;ccc} ----> ccc

The string in the second input can be a list with as many ; as needed (perhaps none at all), not just three as shown. I have almost got what I needed using xstring but it contains superfluous inputs and notation and is basically not elegant!

\newcommand{\variation}[3]{\StrBetween[#1,#2]{;#3;}{;}{;}}

Hence this is what I get

\variation{1}{2}{aaa;bbb;ccc} ----> aaa

It's the second input is the superfluous one.

share|improve this question
    
Welcome to TeX.SX! –  krlmlr Oct 19 '12 at 20:03
add comment

4 Answers

up vote 5 down vote accepted

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

share|improve this answer
    
This works a treat! But what's eTex? –  Geoff Oct 19 '12 at 20:03
    
@Geoff See The eTeX manual. You have it already, because package xstring requires it. –  Heiko Oberdiek Oct 19 '12 at 21:08
    
I have seen that your macro (albeit one now modified by me) gives an unwanted space. \newcommand*{\variation}[2][1]{\StrBetween[{#1},{\numexpr(#1)+1\relax}]{;#2;}{;‌​}{;}\xspace}. Now hello \variation[1]{aaa;bbb;ccc} world doesn't typeset as it should hello aaa world. –  Geoff Oct 20 '12 at 13:13
    
@Geoff Remove \xspace, it does not make any sense here, because the space after the argument braces is already kept. –  Heiko Oberdiek Oct 20 '12 at 14:05
    
Without the \xspace I get as output hello aaaworld. It is the space between hello and aaa that is too large. –  Geoff Oct 20 '12 at 19:21
show 4 more comments

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}
share|improve this answer
    
+1; it would be easy to add a test for a too large first argument. –  egreg Oct 19 '12 at 19:53
    
I cannot get your example to work. It gives me an empty document. –  Geoff Oct 19 '12 at 19:54
    
@egreg warning added :) –  Scott H. Oct 19 '12 at 19:56
    
I'm not sure why it wouldn't work for you Geoff, are you getting any errors? You may need to manually add the xparse and expl3 packages if your setup doesn't add missing packages on the fly. –  Scott H. Oct 19 '12 at 19:58
    
@Geoff It may be that your TeX distribution is too old. What are you using? –  egreg Oct 19 '12 at 20:00
show 5 more comments

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> }
share|improve this answer
add comment

In the general case, the machine may generate a list like

{aaa;bbb; ; ccc}

which will yield a result not intended

\test{\variation{3}{aaa;bbb; ; ccc}} -> [ ]

Also, the parser (; in this case) may be active. These call for the prior normalization of the list.

\documentclass{article}
\usepackage{catoptions}
\makeatletter
% \getstrofnumber{<parser>}{<nr>}{<list>}
\robust@def*\getstrofnumber#1#2#3{%
  \begingroup
  \@tempcnta\z@
  \def\@getstrofnumber##1#1##2\get@nnil{%
    \ifstrcmpTF{##1}\get@nil{%
      \endgroup
      \@latexerr{Item of serial number '#2' not found}\@ehd
    }{%
      \advance\@tempcnta\@ne
      \ifnum#2=\@tempcnta
        \endgroup##1%
        \expandafter\@gobble
      \else
        \expandafter\@iden
      \fi
      {\@getstrofnumber##2\get@nnil}%
    }%
  }%
  \edef\alist{\unexpanded{#3}}%
  \cpt@sttrue\cpt@csvnormalize[#1]\alist
  \expandafter\@getstrofnumber\alist#1\get@nil#1\get@nnil
}
\makeatother

\begin{document}
\def\pput#1{\char`{#1\char`} \space$\rightarrow$\space}
\noindent
\ttfamily
\pput{aaa;bbb; ; ccc}\getstrofnumber{;}{1}{aaa;bbb; ; ccc}\\
\pput{aaa;;bbb;ccc}\getstrofnumber{;}{2}{aaa;;bbb;ccc}\\
\pput{aaa;bbb;ccc}\getstrofnumber{;}{3}{aaa;bbb;ccc}\\
% \pput{aaa;bbb;ccc}\getstrofnumber{;}{4}{aaa;bbb;ccc}% error

\end{document}

enter image description here

share|improve this answer
add comment

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.