7

I have a macro \DefineStrEqCaseMacro which defines a macro that selects a value based on a parameter. With this, I am trying to replace the manual method:

\newcommand*{\SelectBasedOnValue}{beta}% Default Selector
\newcommand{\GreekSelector}[1][\SelectBasedOnValue]{%
    \IfStrEqCase{#1}{%
        {alpha}{\alpha}%
        {beta}{\beta}%
        {gamma}{\gamma}%
    }[\chi]%
}%
%% Define this so we can step through the entire list of possible options
\csdef{GreekSelector List}{alpha, beta, gamma, <default>}%

with:

\DefineStrEqCaseMacro{GreekSelector}%
    [\chi]% <-- Default value in case there is no match
    {%
        {alpha}{\alpha}%
        {beta}{\beta}%
        {gamma}{\gamma}%
    }%

At the end I would like to be able to print ALL the possible values along with the default value (if any). The code below produces the desired result:

enter image description here

However, you notice that in \DefineStrEqCaseMacro, I have hard coded the list of possible values:

\IfValueTF{#2}{%
    %% <> added to list ONLY if a default was specified}
    \csdef{#1 List}{alpha, beta, gamma, <default>}% 
}{%
    \csdef{#1 List}{alpha, beta, gamma}% 
}%

Question:

How do I automatically define this \csdef{} based on #3 provided to this macro. This list should contain all the first options to each pair that \IfStrEqCase expects.

Summary:

Given a parameter of the form:

    {alpha}{\alpha}%
    {beta}{\beta}%
    {gamma}{\gamma}%

with an arbitrary number of elements, how can I define a comma separated list so that I can step through each of the possible matches based on the first parameter in these pairs.

Code:

\documentclass{article}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}% for \foreach

\newcommand*{\SelectBasedOnValue}{beta}

\NewDocumentCommand\DefineStrEqCaseMacro{% Simplified from actual use case
    m% #1 = macro name to define
    o% #2 = default value in case of no match
    m% #3 = IfStrEq case parameters
}{%
    %% https://tex.stackexchange.com/q/226987/4301
    \expandafter\NewDocumentCommand\csname#1\endcsname{%
        s%                      #1 = NOT used here
        O{\SelectBasedOnValue}% #2 = selector
    }{%
        \IfValueTF{#2}{%
            \IfStrEqCase{##2}{%
                #3%
            }[#2]%
        }{%
            \IfStrEqCase{##2}{%
                #3%
            }[\PackageError{\jobname}{Unknown selector "##2"}]%
        }%
    }%
    \IfValueTF{#2}{% ----- ??? How create this list defined here???
        %% <default> added to list ONLY if a default was specified}
        \csdef{#1 List}{alpha, beta, gamma, <default>}% 
    }{%
        \csdef{#1 List}{alpha, beta, gamma}% 
    }%
}%

%%% ---------------------------------------------------------- Manual Method
%%% This is the manual way of doing things, but is error prone
%\newcommand{\GreekSelector}[1][\SelectBasedOnValue]{%
%    \IfStrEqCase{#1}{%
%        {alpha}{\alpha}%
%        {beta}{\beta}%
%        {gamma}{\gamma}%
%    }[\chi]%
%}%
Define this so we can step through the entire list of possible options
%\csdef{GreekSelector List}{alpha, beta, gamma, <default>}%
%%% ----------------------------------------------------------

%% ---------------------------------------------------------- Automated Method
\DefineStrEqCaseMacro{GreekSelector}%
    [\chi]% <-- Default value in case there is no match
    {%
        {alpha}{\alpha}%
        {beta}{\beta}%
        {gamma}{\gamma}%
    }%
%% ---------------------------------------------------------- 

\newcommand*{\TestGreekSelector}{%
    Testing:
    $\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
    $\GreekSelector[alpha]$,
    $\GreekSelector[Unknown]$.% <-- Select default case
}%

\newcommand*{\ShowAllPossibleValues}[1]{%
    \par
    List of ALL options of \texttt{\string#1}:\par
    \edef\MyList{\csuse{#1 List}}%
    \foreach \x in \MyList {
        {\ttfamily\bfseries \x}: $\csuse{#1}[\x]$\par
    }
}%

\begin{document}
    %\TestGreekSelector
    \ShowAllPossibleValues{GreekSelector}%
\end{document}
2

As usual, I wouldn't mix xparse, xstring and etoolbox, as xparse and expl3 already offer the needed tools.

\documentclass{article}
\usepackage{xparse}

\newcommand*{\SelectBasedOnValue}{beta}

\ExplSyntaxOn

% user level commands
\NewDocumentCommand{\defineselector}{mmm}
 {
  % #1 = command name to define, #2 = default, #3 = list to use
  \grill_list_store:nnn { #1 } { #2 } { #3 }
  \grill_list_define:nnn { #1 } { #2 } { #3 }
 }

\NewDocumentCommand{\displayselector}{m+m}
 {% #1 = selector name, #2 = template
  \grill_list_display:nn { #1 } { #2 }
 }

% internal functions
\cs_new_protected:Nn \grill_list_store:nnn
 {
  % massage the list and store it
  \seq_gclear_new:c { g__grill_list_#1_seq }
  \seq_gset_from_clist:cn { g__grill_list_#1_seq } { #3 }
  \tl_gclear_new:c { g__grill_list_#1_tl }
  \tl_gset:cx { g__grill_list_#1_tl }
   {
    \seq_use:cn { g__grill_list_#1_seq } {}
   }
  % store the default value
  \tl_gclear_new:c { g__grill_list_#1_default_tl } 
  \tl_gset:cn { g__grill_list_#1_default_tl } { #2 }
 }

\cs_new_protected:Nn \grill_list_define:nnn
 {
  % define the selector
  \exp_args:Nc \NewDocumentCommand {#1}{O{\SelectBasedOnValue}}
   {
    \str_case_e:nvF { ##1 } { g__grill_list_#1_tl } { #2 }
   }
 }
\cs_generate_variant:Nn \str_case_e:nnF {nv}

\cs_new_protected:Nn \grill_list_display:nn
 {
  % set the auxiliary function to the template
  \cs_set_protected:Nn \__grill_list_show:nn { #2 }
  \par
  List ~ of ~ all ~ options ~ for ~ #1 \par
  \seq_map_inline:cn { g__grill_list_#1_seq }
   {
    \__grill_list_show:nn ##1
   }
  \__grill_list_show:nn { <default> } { \tl_use:c { g__grill_list_#1_default_tl } }
 }

\ExplSyntaxOff

\defineselector{GreekSelector}{\chi}{
  {alpha}{\alpha},
  {beta}{\beta},
  {gamma}{\gamma}
}

\begin{document}

$\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
$\GreekSelector[alpha]$,
$\GreekSelector[Unknown]$.% <-- Select default case

\displayselector{GreekSelector}{\texttt{#1}: $#2$\par}


\end{document}

enter image description here

The second argument to \displayselector is a template for doing the display, where #1 and #2 stand for the first item and the second item in each pair.

A fairly more general way to display the selector: change the definitions of \displayselector and \grill_list_display:nn to

\NewDocumentCommand{\displayselector}{m+m+m+m}
 {% #1 = selector name, #2 = before, #3 = after, #4 = template
  \grill_list_display:nnnn { #1 } { #2 } { #3 } { #4 }
 }

\cs_new_protected:Nn \grill_list_display:nnnn
 {
  % trick to reuse the first argument
  \cs_set_protected:Nn \__grill_list_before:n { #2 }
  % set the auxiliary function to the template
  \cs_set_protected:Nn \__grill_list_show:nn { #4 }
  \__grill_list_before:n { #1 } % before
  \seq_map_function:cN { g__grill_list_#1_seq } \__grill_list_unbrace:n
  \__grill_list_show:nn { <default> } { \tl_use:c { g__grill_list_#1_default_tl } }
  #3 % after
 }
\cs_new:Nn \__grill_list_unbrace:n { \__grill_list_show:nn #1 }

Now you can call the display like

\displayselector{GreekSelector}
 {List of options for #1\par
  \begin{tabular}{@{}ll@{}}}
 {\end{tabular}}
 {\texttt{#1} & $#2$\\}

In the second argument you put what should precede the display, with #1 referring to the first argument (the selector name); in the third argument what should go after. The final argument is the template for each entry.

enter image description here

Complete code for completeness:

\documentclass{article}
\usepackage{xparse}

\newcommand*{\SelectBasedOnValue}{beta}

\ExplSyntaxOn

% user level commands
\NewDocumentCommand{\defineselector}{mmm}
 {
  % #1 = command name to define, #2 = default, #3 = list to use
  \grill_list_store:nnn { #1 } { #2 } { #3 }
  \grill_list_define:nnn { #1 } { #2 } { #3 }
 }

\NewDocumentCommand{\displayselector}{m+m+m+m}
 {% #1 = selector name, #2 = before, #3 = after, #4 = template
  \grill_list_display:nnnn { #1 } { #2 } { #3 } { #4 }
 }

% internal functions
\cs_new_protected:Nn \grill_list_store:nnn
 {
  % massage the list and store it
  \seq_gclear_new:c { g__grill_list_#1_seq }
  \seq_gset_from_clist:cn { g__grill_list_#1_seq } { #3 }
  \tl_gclear_new:c { g__grill_list_#1_tl }
  \tl_gset:cx { g__grill_list_#1_tl }
   {
    \seq_use:cn { g__grill_list_#1_seq } {}
   }
  % store the default value
  \tl_gclear_new:c { g__grill_list_#1_default_tl } 
  \tl_gset:cn { g__grill_list_#1_default_tl } { #2 }
 }

\cs_new_protected:Nn \grill_list_define:nnn
 {
  % define the selector
  \exp_args:Nc \NewDocumentCommand {#1}{O{\SelectBasedOnValue}}
   {
    \str_case_e:nvF { ##1 } { g__grill_list_#1_tl } { #2 }
   }
 }
\cs_generate_variant:Nn \str_case_e:nnF {nv}

\cs_new_protected:Nn \grill_list_display:nnnn
 {
  % trick to reuse the first argument
  \cs_set_protected:Nn \__grill_list_before:n { #2 }
  % set the auxiliary function to the template
  \cs_set_protected:Nn \__grill_list_show:nn { #4 }
  \__grill_list_before:n { #1 } % before
  \seq_map_function:cN { g__grill_list_#1_seq } \__grill_list_unbrace:n
  \__grill_list_show:nn { <default> } { \tl_use:c { g__grill_list_#1_default_tl } }
  #3 % after
 }
\cs_new:Nn \__grill_list_unbrace:n { \__grill_list_show:nn #1 }

\ExplSyntaxOff

\defineselector{GreekSelector}{\chi}{
  {alpha}{\alpha},
  {beta}{\beta},
  {gamma}{\gamma}
}

\begin{document}

$\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
$\GreekSelector[alpha]$,
$\GreekSelector[Unknown]$.% <-- Select default case

\displayselector{GreekSelector}
 {List of options for #1\par
  \begin{tabular}{@{}ll@{}}}
 {\end{tabular}}
 {\texttt{#1} & $#2$\\}

\end{document}

Some explanations

The bulk of the code is \grill_list_store:nnn, where the comma separated list of pairs is massaged. I make a sequence out of it that will be used for the display and also for making a flat list, in the example case it will be

{alpha}{\alpha}{beta}{\beta}{gamma}{\gamma}

which is in the correct format for \str_case_e:nnF. This function should be called

\str_case_e:nnF { test string }
 {
  {case 1}{code}
  {case 2}{code}
  ...
 }
 { code for no match }

Here I use a variant \str_case_e:nvF, where the third argument is a token list name, precisely the flat list built before.

Thus I can define \GreekSelector as shown with \grill_list_define:nnn, passing the desired command name, the default to be used for no match, and the list of pairs.

The macro for displays is a bit tricky. In the simplified form the template is used to define an auxiliary function that's used in \seq_map_inline:cn, where the argument is used to select the massaged sequence.

1

I can offer routines \RemoveEveryKthElement and \UndelimitedListToCommaSpaceList which deliver the result (via \romannumeral-expansion) after two expansion-steps/after two \expandafter-chains.

\RemoveEveryKthElement does remove every K-th element from a list of undelimited (i.e. brace-nested) arguments.

You can, e.g., use it for removing every second element.

\UndelimitedListToCommaSpaceList does transform a list of undelimited arguments into a list where one level of surrounding braces is removed from each element and where as separator a comma and a space is attached behind each element but the last one.

\documentclass{article}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}% for \foreach

%%<begin of copyrighted section>
\makeatletter
%%-----------------------------------------------------------------------------
%% Legal notes
%%.............................................................................
%% As portions of the compilable example which you are currently viewing
%% were delivered by Peter Grill, this work does not consist of the entire 
%% compilable example you are currently viewing but it consists of everything
%% that within this example is nested between
%% a marker  %%<begin of copyrighted section>
%% and a marker  %%<end of copyrighted section>
%% regardless multiple/nested occurrences of such markers. ;-)
%%
%% Copyright (C) 2019 of this work by Ulrich Diez (eu_angelion@web.de)
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%-----------------------------------------------------------------------------
%% Paraphernalia ;-) :
%%.............................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%.............................................................................
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
  {\UD@exchange{\UD@firstoftwo}}{\UD@exchange{\UD@secondoftwo}}%
  {\UD@exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%
%% !!! \UD@ExtractFirstArg's argument must not be empty.     !!!
%%     (You can test for emptiness via \UD@CheckWhetherNull 
%%      before applying \UD@ExtractFirstArg.)
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% Remove every K-th element from list of undelimited/brace-nested arguments,
%% beginning with the L-th element.
%%.............................................................................
%% \RemoveEveryKthElement{<Number K>}%
%%                       {<Number L of the element to begin with>}%
%%                       {<List of undelimited arguments>}%
%%
%% In case <Number K> is smaller than 1 yields: <List of undelimited arguments>
%%   but with surplus space-tokens between elements removed.
%%   (K actually is an ordinal number.
%%   In the context of this macro it is unclear what is meant by non-positive 
%%   ordinal numbers. Thus it is unclear what elements are to be removed from
%%   the list. Thus no elements are removed from the list. Only surplus space 
%%   tokens between elements are removed from the list in this case.)
%% Else:
%%    In case <Number L of the element to begin with> is smaller than 1: 
%%       Error-Message. (The problem with non-positive ordinal numbers
%%       again: A list of elements doesn't have a -1th or 0th
%%       element. So it is unclear what should be the starting-point for
%%       removing elements.)
%%   Else: <List of undelimited arguments> but with the
%%     L-th element and every K-th element following that element
%%     removed.
%%
%%   Caution:
%%   !!! There is no error-checking for detecting whether the arguments
%%        are numbers at all !!!
%%
%% Examples:
%%
%%   \RemoveEveryKthElement{-1}{-4}{{1}{2}{3}  {4}{5} {6} {7} {8} {9}{10}{11}{12}}
%%   yields: {1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}
%%
%%   \RemoveEveryKthElement{2}{-4}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%%   yields: Error-Message.
%%
%%   \RemoveEveryKthElement{4}{4}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%%   yields: {1}{2}{3}{5}{6}{7}{9}{10}{11}
%%
%%   \RemoveEveryKthElement{3}{6}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%%   yields: {1}{2}{3}{4}{5}{7}{8}{10}{11}
%%
%%   \RemoveEveryKthElement{2}{1}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%%   yields: {2}{4}{6}{8}{10}{12}
%%
%%   \RemoveEveryKthElement{2}{2}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%%   yields: {1}{3}{5}{7}{9}{11}
%%
%% Due to \romannumeral-expansion, the result is delivered after two
%% expansion-steps/\expandafter-chains
%%.............................................................................
\newcommand\RemoveEveryKthElement[3]{%
  \romannumeral0%
  \ifnum#1<1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\UD@UndelimitedListRemoveSpacesloop{#3}{}}{%
    \ifnum#2<1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    { \@latex@error{\string\RemoveEveryKthElement: Value of <Number L of the element to begin with> is too small}%
                   {Value of <Number L of the element to begin with> must be positive.}%
    }{%
      \expandafter\UD@PassFirstToSecond\expandafter{%
        \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
        \expandafter\UD@firstoftwo\expandafter{\expandafter}%
        \romannumeral0\number\number#2 000%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{%
          \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
          \expandafter\UD@firstoftwo\expandafter{\expandafter}%
          \romannumeral0\number\number#1 000%
        }{%
          \UD@RemoveEveryKthElementLoop{}%
        }%
      }{#3}{}%
    }%
  }%
}%
\newcommand\UD@UndelimitedListRemoveSpacesloop[2]{%
  \UD@CheckWhetherNull{#1}{ #2}{%
    \UD@CheckWhetherLeadingSpace{#1}{%
      \expandafter\UD@UndelimitedListRemoveSpacesloop
      \expandafter{\UD@removespace#1}{#2}%
    }{%
       \expandafter\UD@PassFirstToSecond\expandafter{%
         \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
         \expandafter\UD@PassFirstToSecond
         \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
         \UD@ExtractFirstArg{#1}{#2}%
       }{%
         \expandafter\UD@UndelimitedListRemoveSpacesloop\expandafter{\UD@firstoftwo{}#1}%
       }%
    }%
  }%
}%
\newcommand\UD@RemoveEveryKthElementLoop[5]{%
  \UD@CheckWhetherNull{#4}{ #5}{%
    \UD@CheckWhetherLeadingSpace{#4}{%
      \expandafter\UD@PassFirstToSecond\expandafter{%
        \UD@removespace#4}{\UD@RemoveEveryKthElementLoop{#1}{#2}{#3}%
      }{#5}%
    }{%
      \UD@CheckWhetherNull{#3}{%
        \UD@CheckWhetherNull{#1}{%
          \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
            \UD@RemoveEveryKthElementLoop{#2}{#2}{}%
          }{#5}%
        }{%
          \expandafter\UD@PassFirstToSecond\expandafter{%
            \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
            \expandafter\UD@PassFirstToSecond
            \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
            \UD@ExtractFirstArg{#4}{#5}}{%
            \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
              \expandafter\UD@RemoveEveryKthElementLoop
              \expandafter{\UD@firstoftwo{}#1}{#2}{}%
            }%
          }%
        }%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{%
          \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
          \expandafter\UD@PassFirstToSecond
          \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
          \UD@ExtractFirstArg{#4}{#5}}{%
          \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
            \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
              \UD@RemoveEveryKthElementLoop{}{#2}%
            }%
          }%
        }%
      }%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% Turn list of undelimited arguments into comma-and-space-list by stripping 
%% surrounding braces from each element and attaching a comma and a space
%% With the last element no comma and no space will be attached.
%%.............................................................................
%%
%%    \UD@UndelimitedListToCommaList{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields:
%%    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
%%
%% No warranties are given for what happens when the items within braces
%% contain commas ,too:
%%
%%    \UD@UndelimitedListToCommaList{{1}{2}{3}{4}{5, A, B, C}{6}{7}{8}{9}{10}{11}{12}}
%% yields:
%%    1, 2, 3, 4, 5, A, B, C, 6, 7, 8, 9, 10, 11, 12
\newcommand\UndelimitedListToCommaSpaceList[1]{%
  \romannumeral0\UD@UndelimitedListToCommaSpaceListloop{#1}{}{}{, }%
}
\newcommand\UD@UndelimitedListToCommaSpaceListloop[4]{%
  \UD@CheckWhetherNull{#1}{ #2}{%
    \UD@CheckWhetherLeadingSpace{#1}{%
      \expandafter\UD@UndelimitedListToCommaSpaceListloop
      \expandafter{%
      \UD@removespace#1}{#2}{#3}{#4}%
    }{%
       \expandafter\UD@PassFirstToSecond\expandafter{%
         \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
         \expandafter\UD@exchange
         \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
         \UD@ExtractFirstArg{#1}{#2#3}%
       }{%
         \expandafter\UD@UndelimitedListToCommaSpaceListloop\expandafter{\UD@firstoftwo{}#1}%
       }{#4}{#4}%
    }%
  }%
}%
%%-----------------------------------------------------------------------------     
\makeatother
%%<end of copyrighted section>

\newcommand*{\SelectBasedOnValue}{beta}

\NewDocumentCommand\DefineStrEqCaseMacro{% Simplified from actual use case
    m% #1 = macro name to define
    o% #2 = default value in case of no match
    m% #3 = IfStrEq case parameters
}{%
    %% https://tex.stackexchange.com/q/226987/4301
    \expandafter\NewDocumentCommand\csname#1\endcsname{%
        s%                      #1 = NOT used here
        O{\SelectBasedOnValue}% #2 = selector
    }{%
        \IfValueTF{#2}{%
            \IfStrEqCase{##2}{%
                #3%
            }[#2]%
        }{%
            \IfStrEqCase{##2}{%
                #3%
            }[\PackageError{\jobname}{Unknown selector "##2"}]%
        }%
    }%
    %%-- This is how one could probably create this list. ;-) 
    %% As you can define an option whose name is "<default>", I would
    %% not add the default-value to the list but mention it separately.
    %% Besides this one might specify emptiness as default.
    %% Therefore I decided to have the macro #1default defined not in
    %% terms of \long only when no value was specified.
    %% For distinguising default being specified as emptiness from
    %% default not being specified at all, compare the default-macro
    %% to \empty.
    %% Also I'd not use `\csdef` as that doesn't throw an error when
    %% overriding a definition that already exists
    \csname UD@PassFirstToSecond\expandafter\endcsname\expandafter{%
      \romannumeral0%
        \csuse{UD@exchange}{ }{%
          \expandafter\expandafter\expandafter\expandafter
          \expandafter\expandafter\expandafter
        }%
        \expandafter\UndelimitedListToCommaSpaceList\expandafter{%
          \romannumeral0\csuse{UD@exchange}{ }{%
            \expandafter\expandafter\expandafter
          }%
          \RemoveEveryKthElement{2}{2}{#3}%
        }%
    }%
    {\expandafter\newcommand\csname#1 List\endcsname}%
    \IfValueTF{#2}%
              {\expandafter\newcommand\csname#1 default\endcsname{#2}}%
              {\expandafter\newcommand\expandafter*\csname#1 default\endcsname{}}%
}%

%% ---------------------------------------------------------- Automated Method
\DefineStrEqCaseMacro{GreekSelector}%
    [\chi]% <-- Default value in case there is no match
    {%
        {alpha}{\alpha}%
        {beta}{\beta}%
        {gamma}{\gamma}%
    }%
%% ---------------------------------------------------------- 

\newcommand*{\TestGreekSelector}{%
    Testing:
    $\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
    $\GreekSelector[alpha]$,
    $\GreekSelector[Unknown]$.% <-- Select default case
}%

\newcommand*{\ShowAllPossibleValues}[1]{%
    \par\noindent
    List of ALL options of \texttt{\string#1}:%
    \edef\MyList{\csuse{#1 List}}%
    \foreach \x in \MyList {%
        \par\noindent
        {\ttfamily\bfseries \x}: $\csuse{#1}[\x]$%
    }%
    \par\noindent
    \hrulefill\null
    \par\noindent
    \textit{$\langle$standard-option$\rangle$}:
    {\ttfamily\bfseries\SelectBasedOnValue}: $\csuse{#1}$%
    \ifcsequal{#1 default}{empty}{}{%
      \par\noindent
      \textit{$\langle$default-value$\rangle$}: $\csuse{#1 default}$%
    }%
    \smallskip
    \par\noindent
    {\footnotesize
      (The \textit{$\langle$standard-option$\rangle$} is used when the
       user did not specify any option.\\%
       The \textit{$\langle$standard-option$\rangle$} can be changed via
       redefining the macro \texttt{\string\SelectBasedOnValue}.%
       \ifcsequal{#1 default}{empty}{}{%
         \\The \textit{$\langle$default-value$\rangle$} is used when
         the user did specify an unknown option.%
       }%
      )%
    }\par
}%

\begin{document}
\TestGreekSelector

\bigskip

\ShowAllPossibleValues{GreekSelector}%
\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.