# Equivalent to “in” operator

I want to write a command that responds to an argument being in one set of reference strings or another.

The argument takes a (musical) note name, and the command will print an accidental depending on whether the argument is in (a, h, c, d, e, f, g), (ces, des, es, fes, ges, as, b) or (cis, dis, eis, fis, gis, ais, his) (German note names).

In a programming language I would use the in operator to achieve this, but I don't know how to do it in LaTeX.

What I would like to write is

\notewithacc{c}\\
\notewithacc{cis}\\
\notewithacc{des}


and get

\natural c\\
\sharp cis\\
\flat des


as the output.

-
– egreg Jan 22 '14 at 11:45

The following example uses \in@{<substring>}{<string>} that tests, whether <substring> is part of <string>. After the test, the result is provided by the switch \ifin@.

\documentclass{article}

% List with notes
\newcommand*{\NotesNatural}{a, h, c, d, e, f, g}
\newcommand*{\NotesFlat}{ces, des, es, fes, ges, as, b}
\newcommand*{\NotesSharp}{cis, dis, eis, fis, gis, ais, his}

\makeatletter

% Sanitize note lists
\newcommand*{\NotesNormalize}[1]{%
\edef#1{%
,\expandafter\zap@space#1 \@empty,%
}%
}

\NotesNormalize{\NotesNatural}
\NotesNormalize{\NotesFlat}
\NotesNormalize{\NotesSharp}

% \NotesTest{<note>}{<note list>}{<yes>}{<no>}
\newcommand*{\NotesTest}[2]{%
\edef\NotesTemp{%
\noexpand\in@{,#1,}{#2}%
}\NotesTemp
\ifin@
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\newcommand*{\notewithacc}[1]{%
\in@{,}{#1}%
\ifin@
?#1%
\else
\NotesTest{#1}{\NotesNatural}{%
$\m@th\natural$#1%
}{%
\NotesTest{#1}{\NotesFlat}{%
$\m@th\flat$#1%
}{%
\NotesTest{#1}{\NotesSharp}{%
$\m@th\sharp$#1%
}{%
?#1%
}%
}%
}%
\fi
}
\makeatother

\begin{document}
\noindent
\notewithacc{c}\\
\notewithacc{cis}\\
\notewithacc{des}\\
\notewithacc{x}\\
\notewithacc{dis,eis}
\end{document}


Remarks:

• \zap@space removes spaces in the sanitizing step from the note lists.
• Also the lists all start and ends with commas. This way, the substring with the searched note is surrounded by commas. This ensures exact matches.
• BTW, I do not know, why accidentals are math symbols in LaTeX. \m@th removes surrounding space if \mathsurround is set.
• The updated solution also checks, whether there is a comma in the argument of \notewithacc as in \notewithacc{dis,eis}.
-
Thank you for this solution. It works perfectly, although I don't fully understand it. The math/spacing issue is dealt with fontspec/lilyglyphs in the real files BTW (looks better anyway). – uli_1973 Jan 22 '14 at 12:32
Is there an easy way to recognize the input like \notewithacc{dis,eis} as an error as well? – g.kov Jan 22 '14 at 14:51
@g.kov: Yes, I have updated the answer by adding a check for the comma in the argument of \notewithacc. – Heiko Oberdiek Jan 22 '14 at 15:56

Taking a similar approach to Heiko's answer but using the LaTeX3 programming layer, expl3, might lead to something like:

\documentclass{article}

\usepackage{expl3,xparse}
\ExplSyntaxOn
\makeatletter
% Comma lists of notes
\clist_const:Nn \c_ulinotes_natural_clist {a, h, c, d, e, f, g}
\clist_const:Nn \c_ulinotes_flat_clist {ces, des, es, fes, ges, as, b}
\clist_const:Nn \c_ulinotes_sharp_clist {cis, dis, eis, fis, gis, ais, his}

\cs_new_protected:Npn \ulinotes_note:n #1
{
\clist_if_in:NnT \c_ulinotes_natural_clist {#1}
{ \c_math_toggle_token \m@th \natural \c_math_toggle_token }
\clist_if_in:NnT \c_ulinotes_flat_clist {#1}
{ \c_math_toggle_token \m@th \flat \c_math_toggle_token }
\clist_if_in:NnT \c_ulinotes_sharp_clist {#1}
{ \c_math_toggle_token \m@th \sharp \c_math_toggle_token }
#1
}

\NewDocumentCommand { \notewithacc } { >{\TrimSpaces} m }
{ \ulinotes_note:n {#1} }
\makeatother
\ExplSyntaxOff

\begin{document}
\noindent
\notewithacc{c}\\
\notewithacc{cis}\\
\notewithacc{des}\\
\notewithacc{x}
\end{document}


This uses the expl3 tests for comma-list membership along with a built-in command to strip spaces from arguments when 'grabbed'. As in the 'standard' LaTeX2e approach we have to allow for the fact that math mode is required for the symbols, which currently means using a bit of LaTex2e code (\m@th).

-
I might raise the space question for searching with the team :-) – Joseph Wright Jan 22 '14 at 12:12
Thank you. I could accept this answer too but take the other because I'm still stuck with "old" latex ;-) – uli_1973 Jan 22 '14 at 12:30

MWE using \csname mapping:

\documentclass{article}

\makeatletter
\@for\@n@te:=a,h,c,d,e,f,g\do{\expandafter\def\csname n@te(\@n@te)\endcsname{\natural}}
\@for\@n@te:=ces,des,es,fes,ges,as,b\do{\expandafter\def\csname n@te(\@n@te)\endcsname{\flat}}
\@for\@n@te:=cis,dis,eis,fis,gis,ais,his\do{\expandafter\def\csname n@te(\@n@te)\endcsname{\sharp}}

\def\notewithacc#1{%
\ifcsname n@te(#1)\endcsname%
\ensuremath{\csname n@te(#1)\endcsname}#1%
\else ?#1 %
\fi
}
\makeatother

\begin{document}

\makeatletter
\begin{itemize}
\item \@for\@n@te:=a,h,c,d,e,f,g\do{\notewithacc{\@n@te} }
\item \@for\@n@te:=ces,des,es,fes,ges,as,b\do{\notewithacc{\@n@te} }
\item \@for\@n@te:=cis,dis,eis,fis,gis,ais,his\do{\notewithacc{\@n@te} }
\item \@for\@n@te:=x,fi,{dis,eis}\do{\notewithacc{\@n@te} }
\end{itemize}
\makeatother

\notewithacc{des}

\end{document}


-