# Split a string and apply an operation to each segment (or “word”)

I'm currently writing a tutorial for some software, and I'd like to refer to the shortcuts used in this program. These shortcuts should be notable, i.e. displayed in a different color or font style.

Basically what I'm trying to do is create a new command, with only one argument -- a string of words, separated by some delimiter (comma). Each word (or segment, since in this case words would be substrings like Ctrl, Shift, Enter or just S, O) should be displayed in the notable style (let's say \textbf{}), and between each two words there should be a + (or some other symbol) using the regular font style. Note that this isn't necessarily the same delimiter used in the argument (the comma)!

In pseudo code:

\newcommand{\shortcut}[1]{
For every word in the argument #1:
- Display in notable font style
- Add + in regular font style (except for last word)
}


So \shortcut{Ctrl,Shift,Z} should result in \textbf{Ctrl}+\textbf{Shift}+\textbf{Z}.

Any recommendations for packages to use, or can this be done using relatively simple LaTeX?

-

## "Old style" method

\makeatletter
\newcommand{\shortcut}[1]{%
\@tempswafalse
\@for\next:=#1\do
{\if@tempswa+\else\@tempswatrue\fi\textbf{\next}}%
}
\makeatother


Then \shortcut{Ctrl,Shift,Z} will do what you want.

## "New style" method

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\shortcut}{O{,}m}
{
\ailurus_make:nn {#1} {#2}
}

\cs_new_protected:Npn \ailurus_make:nn #1 #2
{
\seq_set_split:Nnn \l_ailurus_args_seq { #1 } { #2 }
\seq_pop_left:NN \l_ailurus_args_seq \l_ailurus_temp_tl
\textbf { \l_ailurus_temp_tl }
\seq_map_inline:Nn \l_ailurus_args_seq { + \textbf { ##1 } }
}

\seq_new:N \l_ailurus_args_seq
\tl_new:N \l_ailurus_temp_tl
\ExplSyntaxOff

\begin{document}
\shortcut{Ctrl,Shift,Z}

\shortcut[/]{Ctrl/,}
\end{document}


It seems more complicated, but it's really very similar. The input is split at the commas and then a cycle is performed. The first element is popped off and treated specially (not very much, actually, in this case).

Bonus With this solution you can easily print a comma, by choosing a different delimiter as optional argument. Of course also

\shortcut{Ctrl,{,}}


would have worked, but this strategy can be used also in more difficult cases.

-
Thanks! The first (why is this called the 'old' one by the way) solution is indeed easier to read/understand. I take it the @tempswa flag is just a predefined boolean flag? And @tempswatrue and @tempswafalse set the value to respectively true and false? – Ailurus Jun 14 '12 at 21:50
The 'new style' looks interesting too, never saw this used before (but then again I'm clearly not that experienced writing my own commands). – Ailurus Jun 14 '12 at 21:51
@Ailurus \if@tempswa is a "scratch conditional" defined by the LaTeX kernel for tasks like this, where its value (true or false) is needed for a short job; one should always set it to true or false before starting the job, as its value can be any. – egreg Jun 14 '12 at 22:04
One other comment: when using the 'old style' method, an additional space is added after the shortcut. This doesn't happen when I rewrite the \newcommand{} without linebreaks. Apparently I have to add a % after the if statement in your code, but why? – Ailurus Jun 14 '12 at 22:25
@Ailurus Yes, sorry, a % was missing. The end of line became part of the definition (and is converted to a space), so it was added at each call of the macro. The "new style" method doesn't suffer of this problem because all spaces and end-of-lines are ignored between \ExplSyntaxOn and \ExplSyntaxOff. – egreg Jun 14 '12 at 22:30
\documentclass{article}

\makeatletter

\def\shortcut#1{{\def\scsep{\def\scsep{+}}\@for\w:=#1\do{\scsep\textbf{\w}}}}
\makeatother

\begin{document}

\shortcut{Ctrl,Shift,Z}

\end{document}

-
Very concise, thanks! But how does the \def\scsep{} part work? I don't understand why the + isn't printed the first time. – Ailurus Jun 14 '12 at 21:43
The first time \scsep doesn't print anything it just defines \scsep to print. It's a trick way of avoiding a + on the first time. If you look in the current associated forum you'll see @egreg scurrilously commenting it's not the first time I've used that idiom on this site:-) – David Carlisle Jun 14 '12 at 21:53

Almost any list-processing package would be able to help here, including LaTeX3's list processing constructs. Here's one using etoolbox:

\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\makeatletter
\newcommand{\shortcut}[1]{%
\begingroup%
% count the elements
\@tempcnta=\z@
\docsvlist{#1}%
\chardef\@listsize\@tempcnta%
\@tempcnta=\@ne%
\renewcommand*\do[1]{%
\ifnum\@tempcnta<\@listsize
\else
\unpenalty\texttt{##1}%
\fi}%
\docsvlist{#1}%
\endgroup%
}
\makeatother
\begin{document}
You can use \shortcut{Ctrl,Shift,Z} or \shortcut{2,2,1,2,Alt,X} and on occasion \shortcut{Ctrl,Alt,Del}.
\end{document}​


Taken from Special formatting of a mathmode list. The introduction of a zero \penalty allows for breaking at the text block boundary if your sequence is long.

-
Nice touch with the \penalty part. And thanks for suggesting \texttt{} instead of \textbf! – Ailurus Jun 14 '12 at 21:53

You can use a \foreach loop from the pgffor package, which is pare of the pgf package:

## Code:

\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgffor}

\newtoggle{FirstShortcut}
\newcommand*{\shortcut}[1]{%
\global\toggletrue{FirstShortcut}%
\foreach \member in {#1} {%
\iftoggle{FirstShortcut}{}{+}%
\textbf{\member}%
\global\togglefalse{FirstShortcut}%
}%
}%

\begin{document}
\shortcut{Ctrl,Shift,Z}
\end{document}

-

... and a really short one, using only one 'special' macro :) (requires xstring):

\documentclass[10pt,a4paper]{article}

\usepackage{xstring}

\newcommand{\specialtext}[1]{%
\StrSubstitute{#1}{,}{ + }[\myspecialtext]
\textbf{\myspecialtext}}

\begin{document}
This is a \specialtext{very,special} command.
\end{document}


EDIT: As @PeterGrill pointed out, this will produce a bold face +. To avoid this, as the request of the OP was (in case this is necessary), the code should be modified like this, without sacrificing any comfort:

\newcommand{\specialtext}[1]{%
\noexpandarg
\StrSubstitute{#1}{,}{ \textnormal{+} }[\myspecialtext]
\textbf{\myspecialtext}}

-
But I think the original requirement (which may or many not be significant) was that the \textbf is applied only to the text and not the +. – Peter Grill Jun 15 '12 at 21:28
@PeterGrill One can cheat and write ${}+{}$ instead of <space>+<space>. – egreg Jun 15 '12 at 22:02
@egreg: I added another 'cheat code', but I like yours too. :) – Count Zero Jun 15 '12 at 22:07
@egreg: Very clever cheat, although I like CountZero's cheat as it is less magical. – Peter Grill Jun 15 '12 at 22:25

Another one, using lambda-lists:

\documentclass{article}
\usepackage{lambda} % http://www.ctan.org/pkg/lambda-lists
\makeatletter
\def\Unlistize#1{#1\Unlistize@{}}
\def\Unlistize@#1{#1\Foldr\Plusize{}}
\makeatother
\def\Plusize#1#2{+#1#2}
\def\shortcut#1{\Show\Map\textbf[#1]}

\begin{document}

\shortcut{Ctrl,Alt,Del}
\shortcut{Ctrl,Shift,Z}

\end{document}

-