# How can I make a case-insensitive compare with expl3?

I am in the process of writing a macro which takes a 4 digit hex value and does something based on the input. I want to achieve, that a user can use uppercase or lowercase characters for the hex code. What already works is:

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\NewDocumentCommand{\actionForHEX}{m}{%
\str_case:nnF {#1}
{
{E085} {This is E085}
{E086} {This is E086}
}
{Unknown}
}
\ExplSyntaxOff

\begin{document}
Works: \actionForHEX{E085}

Does not work: \actionForHEX{e085}
\end{document}


So in principal I can define actions, but I do not understand the use of \str_flipcase:n to allow the case to work for lowercase as well.

Based on Expandable case insensitive switch case for string comparison I tried something, but this had no success at all:

\ExplSyntaxOn
\NewDocumentCommand{\actionForHEX}{m}{%
\str_case_e:nn { \str_foldcase:e { #1 } }
{
{E085} {This is E085}
{E086} {This is E086}
}
}
\ExplSyntaxOff


Ok, based on your input I tried my luck again, but it seems I still don't understand something. This is my new approach:

\documentclass{article}

\usepackage{expl3,xparse}

\ExplSyntaxOn
\NewDocumentCommand{\actionForHEX}{m}{%
\str_case:nnF { \str_foldcase:n { #1 } }
{
{e085} {This~is~E085}
{e086} {This~is~E086}
}
{Cannot~find~it}
}
\ExplSyntaxOff

\begin{document}
Does not work: \actionForHEX{E085}

Does not work: \actionForHEX{e085}
\end{document}


This results in Canot find it for both calls of \actionForHEX

• Shouldn't your test cases be e085 and e086, resp.? – cgnieder May 11 at 3:59
• What cgnieder says: With \str_foldcase you case-fold the input, now your comparison text must be case-folded as well. Case folding is largely based on lowercase, so you need to compare against e085 and e086. If you want to use \str_foldcase:e you'll need \cs_generate_variant:Nn \str_foldcase:n { e } as in the linked answer. You probably also want to write This~is~E085 to get spaces between the words. – moewe May 11 at 4:52
• @moewe Looks like an answer to me – Joseph Wright May 11 at 8:35
• I actually don't need to expand macros here, I will only use straight inputs. I attached a new approach, but this makes it worse, not better, I don't get, why. Do I need \str_case_e:nnF {\str_foldcase:n{#1}} ? This results in an undefined control sequence for \actionForHEX – TobiBS May 11 at 10:08
• @TobiBS Yes, but you do need to expand \str_foldcase:n, otherwise the comparison will be between the literal strings \str_foldcase:n{e085} and e085. Try \exp_args:Ne \str_case:nnF { \str_foldcase:n { #1 } } – Phelype Oleinik May 11 at 11:51

As explained in the comments by cgnieder you need to compare against e085 and e086, respectively, since you are comparing against the case-folded input (\str_foldcase:n { #1 }). Broadly speaking, case folding is based on lowercase, so the input E085 gets folded into e085 and that's what you need to compare against.

Because we apply a macro to the input, we can't directly compare the strings with as normal n arguments. We need to make sure that \str_foldcase:n { #1 } is expanded first.

This can be done as in Skillmon likes topanswers.xyz's answer to Expandable case insensitive switch case for string comparison with \str_case_e:nn

\ExplSyntaxOn
\NewDocumentCommand{\actionForHEX}{m}{%
\str_case_e:nn { \str_foldcase:n { #1 } }
{
{e085} {This~is~E085}
{e086} {This~is~E086}
}
}
\ExplSyntaxOff


That command will fully expand all involved strings with the new e-type expansion. (So \str_foldcase:n { #1 } is expanded, which is nice. And e085 and e086 are expanded as well, which is a bit pointless).

We can avoid the the expansion of e085 and e086 by generating the en-variant of \str_case:nn, which only expands \str_foldcase:n { #1 }

\ExplSyntaxOn
\cs_generate_variant:Nn \str_case:nn { e }

\NewDocumentCommand{\actionForHEX}{m}{%
\str_case:en { \str_foldcase:n { #1 } }
{
{e085} {This~is~E085}
{e086} {This~is~E086}
}
}
\ExplSyntaxOff


If you are using an old system where e-type expansion is not available, you can try x-type expansion.

\documentclass{article}
\usepackage{expl3,xparse}

\ExplSyntaxOn
\cs_generate_variant:Nn \str_case:nn { x }

\NewDocumentCommand{\actionForHEX}{m}{%
\str_case:xn { \str_foldcase:n { #1 } }
{
{e085} {This~is~E085}
{e086} {This~is~E086}
}
}
\ExplSyntaxOff

\begin{document}
\actionForHEX{E085}

\actionForHEX{e085}

\actionForHEX{E086}

\actionForHEX{e086}
\end{document}


On older systems \str_fold_case:n (introduced before November 2016, see l3news10) may work instead of \str_foldcase:n (which replaced \str_fold_case on 2019-11-26).

If you want to include an F non-matching branch at the end, it is better to generate the variants with \prg_generate_conditional_variant:Nnn (thanks to Phelype Oleinik for explaining that in the comments). For e-type expansion that would look as follows

\documentclass{article}
\usepackage{expl3,xparse}

\ExplSyntaxOn
\prg_generate_conditional_variant:Nnn \str_case:nn { e } { F }

\NewDocumentCommand{\actionForHEX}{m}{%
\str_case:enF { \str_foldcase:n { #1 } }
{
{e085} {This~is~E085}
{e086} {This~is~E086}
}
{Not~matched}
}
\ExplSyntaxOff

\begin{document}
\actionForHEX{E085}

\actionForHEX{e085}

\actionForHEX{E086}

\actionForHEX{e086}

\actionForHEX{E087}
\end{document}


It would be analogous for x-type expansion (and possibly \str_fold_case:n on older systems).

• Thanks @moewe, I am still a total newbie to expl3 and this helped me a lot to understand. I was now also able to expand your solution to a variant with a false tree: \ExplSyntaxOn \cs_generate_variant:Nn \str_case:nnF {xn} \NewDocumentCommand{\actionForHEX}{m}{% \str_case:xnF {\str_fold_case:n{#1}} { {e085} {This~is~E085} {e086} {This~is~E086} } {Alternative} } \ExplSyntaxOff – TobiBS May 12 at 11:46
• @TobiBS The code you show should work (it does for me) however for branching conditionals you should use \prg_generate_conditional_variant:Nnn \str_case:nn { x } { F } instead. The first argument is the function without the conditionals in the signature, the second is the list of variants (same as for \cs_generate_variant:Nn), and the third is the list of branching conditionals. Note also that trailing n and N are redundant when generating variants and can be omitted. – Phelype Oleinik May 12 at 12:32
• @PhelypeOleinik So prg_generate_conditional_variant is benefitial, because it does not expand the false clause, is that the point? Does this change the line \str_case:xnF {\str_fold_case:n{#1}}? – TobiBS May 13 at 11:14
• @TobiBS I'm not sure I understand your question... Using \cs_generate_variant:Nn in a conditional function like \str_case:nnF works because of how it's implemented (and that's probably not going to change), but it's conceptually incorrect. Since \str_case:nnF is a conditional, you should use \prg_generate_conditional_variant:Nnn (which also makes it easier if you want to define also the T, TF, and p forms). But as I said, the code you posted in your first comment should work as well... – Phelype Oleinik May 13 at 11:55
• @TobiBS Yeah, expl3 has advanced quite a lot in the past few years, so 2017 is rather outdated. – Phelype Oleinik May 13 at 14:57