add tag
samcarter
> This is part of the Summer of Code 2022 series, see https://topanswers.xyz/tex?q=2059 for more information

Sort the following list of names in alphabetical order:

```
Scrooge McDuck
Huey Duck
Dewey Duck
Louie Duck
Webby Vanderquack
Launchpad McQuack
Bentina Beakley
Donald Duck
Della Duck
```

Sort by family name first. If the family name happens to be the same, sort these cases further by given name. 


![SoC.png](/image?hash=b21363f708bb631cfa7b324c42a59289dbfbd84213844143e88a0c78f7a9da97)
Top Answer
frougon
\* This line is not a spoiler. \*

Here is my solution using `\seq_sort:Nn`. As written in the comments, my code implements a case-insensitive comparison (see `\__socviii_head_charcode:n` which uses `\char_value_lccode:n`):

```
\documentclass{article}
\usepackage[T1]{fontenc} % only for the
\usepackage{xcolor}      % \highlight macro

\ExplSyntaxOn

% Expand to the lowercase code of the first item of #1. This item is expected
% to contain a single character token.
\cs_new:Npn \__socviii_head_charcode:n #1
  {
    \char_value_lccode:n
      { \exp_after:wN ` \tl_head:w #1 { } \q_stop \c_space_token }
  }

\cs_generate_variant:Nn \__socviii_head_charcode:n { V }

\bool_new:N \l__socviii_compare_words_finished_bool

% Set int var #1 to -1, 0 or 1 depending on how token lists #2 and #3 compare
% lexicographically (based on the lowercase codes of the TeX-internal
% character codes of the character tokens #2 and #3 are made of). The
% comparison is case-insensitive. The result in #1 is encoded as follows:
% -1 means #2 < #3; 0 means #2 compares equal to #3; 1 means #2 > #3.
\cs_new_protected:Npn \socviii_compare_words:Nnn #1#2#3
  {
    \tl_set:Nn \l_tmpa_tl {#2}
    \tl_set:Nn \l_tmpb_tl {#3}
    \int_zero:N #1
    \bool_set_false:N \l__socviii_compare_words_finished_bool

    \bool_do_until:Nn \l__socviii_compare_words_finished_bool
      {
        \tl_if_empty:NTF \l_tmpa_tl
          {
            \tl_if_empty:NTF \l_tmpb_tl
              { \bool_set_true:N \l__socviii_compare_words_finished_bool }
              {
                \int_set:Nn #1 { -1 }
                \bool_set_true:N \l__socviii_compare_words_finished_bool
              }
          }
          {
            \tl_if_empty:NTF \l_tmpb_tl
              {
                \int_set:Nn #1 { 1 }
                \bool_set_true:N \l__socviii_compare_words_finished_bool
              }
              {                 % \l_tmpa_tl and \l_tmpb_tl are both non-empty
                \int_set:Nn #1
                  {
                    \int_sign:n { \__socviii_head_charcode:V \l_tmpa_tl -
                                  \__socviii_head_charcode:V \l_tmpb_tl }
                  }
                \int_compare:nNnTF { #1 } = { 0 }
                  {
                    \tl_set:Nx \l_tmpa_tl { \tl_tail:N \l_tmpa_tl }
                    \tl_set:Nx \l_tmpb_tl { \tl_tail:N \l_tmpb_tl }
                  }
                  { \bool_set_true:N \l__socviii_compare_words_finished_bool }
              }
          }
      }
  }

\cs_generate_variant:Nn \socviii_compare_words:Nnn { NVV }

\seq_new:N \l__socviii_sf_a_seq
\seq_new:N \l__socviii_sf_b_seq
\tl_new:N \l__socviii_sf_a_first_tl  % first word of item A
\tl_new:N \l__socviii_sf_b_first_tl  % first word of item B
\tl_new:N \l__socviii_sf_a_second_tl % second word of item A
\tl_new:N \l__socviii_sf_b_second_tl % second word of item B

% Sort function for two items of the form “FirstName LastName”
\cs_new_protected:Npn \socviii_sort_func:nn #1#2
  {
    \seq_set_split:Nnn \l__socviii_sf_a_seq { ~ } {#1}
    \seq_set_split:Nnn \l__socviii_sf_b_seq { ~ } {#2}

    \seq_pop_left:NN \l__socviii_sf_a_seq \l__socviii_sf_a_first_tl
    \seq_pop_left:NN \l__socviii_sf_a_seq \l__socviii_sf_a_second_tl
    \seq_pop_left:NN \l__socviii_sf_b_seq \l__socviii_sf_b_first_tl
    \seq_pop_left:NN \l__socviii_sf_b_seq \l__socviii_sf_b_second_tl

    % Compare the family names
    \socviii_compare_words:NVV \l_tmpa_int \l__socviii_sf_a_second_tl
      \l__socviii_sf_b_second_tl

    \int_case:nnF { \l_tmpa_int }
      {
        { -1 } { \sort_return_same: }
        { 0 }
          { % Compare the first names
            \socviii_compare_words:NVV \l_tmpa_int \l__socviii_sf_a_first_tl
              \l__socviii_sf_b_first_tl
            \int_compare:nNnTF { \l_tmpa_int } > { 0 }
              { \sort_return_swapped: }
              { \sort_return_same: }
          }
        { 1 } { \sort_return_swapped: }
      }
      { \IfThisIsEverExpandedThisIsABug }
  }

\seq_new:N \l__socviii_stsn_seq % temporary seq var

\msg_new:nnn { socviii } { list-item-not-made-of-two-words }
  { List~ item~ '#1'~ is~ not~ made~ of~ two~ words. }

% #1: clist var where the sorted result will be stored
% #2: comma list in which each item must contain, once spaces have been
%     trimmed from both sides, exactly two words separated by an explicit
%     space token.
\cs_new_protected:Npn \socviii_set_to_sorted_names:Nn #1#2
  {
    \seq_clear:N \l__socviii_stsn_seq
    \clist_map_inline:nn {#2}
      { \seq_put_right:Nx \l__socviii_stsn_seq { \tl_trim_spaces:n {##1} } }

    % Input sanity check
    \seq_map_inline:Nn \l__socviii_stsn_seq
      {
        \seq_set_split:Nnn \l_tmpa_seq { ~ } {##1}
        \int_compare:nNnF { \seq_count:N \l_tmpa_seq } = { 2 }
          {
            \msg_error:nnn { socviii } { list-item-not-made-of-two-words } {##1}
          }
      }

    \seq_sort:Nn \l__socviii_stsn_seq { \socviii_sort_func:nn {##1} {##2} }
    \clist_set_from_seq:NN #1 \l__socviii_stsn_seq
  }

\cs_new_eq:NN \setToSortedNames \socviii_set_to_sorted_names:Nn
\cs_new_eq:NN \clistMapInline \clist_map_inline:Nn

\ExplSyntaxOff

\newcommand*{\highlight}[1]{%
  \textcolor{blue}{|}% used to show that spaces are trimmed
  #1%
  \textcolor{blue}{|}% on both sides of each item
}

\begin{document}

\setToSortedNames{\result}{ % <--- space token will be trimmed if present
  Scrooge McDuck ,          % ditto here
  Huey Duck,
  Dewey Duck,
  Louie Duck ,              % etc.
  Webby Vanderquack,
  Launchpad McQuack,
  Bentina Beakley,
  Donald Duck,
  Della Duck                % and here too (i.e., on both sides of each item)
}%
\clistMapInline{\result}{%
  \highlight{#1}\par
}

\end{document}
```

![image.png](/image?hash=f2fa561dd5bf6db7f1da6843aa43dbdb89033dcb64e70943947035df184f2b4e)
Answer #2
Skillmon
Duck wants to solve:

The following defines the expandable macro `\namesort` that takes two arguments, a separator inserted between each list element after sorting, and the comma separated list of names to be sorted. A name must contain one space delimiting given from surname. These two blocks are then simply swapped for the string comparison using the `\pdfstrcmp` primitive. There is a small speed optimisation in the code of `\namesort_sort:nnTF`, to get the optimisation done by `\prg_new_conditional:Npnn` we have to end in `\prg_return_true: \else: \prg_return_false: \fi:`, and in order to get that the conditional is inverted by `\__namesort_turn_false:w`.

This  uses code from an unmerged PR of mine that adds the expandable macro `\clist_sort:nN` to `expl3` (and speeds up `\tl_sort:nN`). The only altered line from the PR (except for expanding the `@@` `l3docstrip`-notation) is the one defining `\tl_sort:nN` (which isn't even used here). The code of the PR is indented by four spaces, the actual code for this answer boils down to the last 13 lines of the `\ExplSyntaxOn`-block, and the stuff outside of `\ExplSyntaxOn`.

```
\documentclass{article}

\usepackage{ducksay}

\ExplSyntaxOn
    \scan_new:N \s__sort_merge_exp_mark
    \scan_new:N \s__sort_merge_exp_stop
    \scan_new:N \s__sort_merge_exp_tail
    \scan_new:N \s__sort_merge_exp_list
    \cs_gset:Npn \tl_sort:nN #1#2
      {
        \__kernel_exp_not:w
          \exp_after:wN \__sort_merge_exp_after_bottom_up_tl:Nw \exp_after:wN #2
          \tex_expanded:D
            {
              \__sort_merge_exp_bottom_up_tl:Nnn
                #2 #1 \s__sort_merge_exp_mark \s__sort_merge_exp_stop
            }
      }
    \cs_new:Npn \__sort_if_stop:w #1 \s__sort_merge_exp_stop {}
    \cs_new:Npn \__sort_if_mark:w #1 \s__sort_merge_exp_mark {}
    \cs_new:Npn \__sort_if_tail:w #1 \s__sort_merge_exp_tail {}
    \cs_new:Npn \__sort_merge_exp_bottom_up_tl:Nnn #1#2#3
      {
        \__sort_if_stop:w #3 \__sort_merge_exp_bottom_up_tl_stop:w \s__sort_merge_exp_stop
        \__sort_if_mark:w #3 \__sort_merge_exp_bottom_up_tl_mark:w \s__sort_merge_exp_mark
        #1 {#2} {#3}
          { \__kernel_exp_not:w { {#2} {#3} } }
          { \__kernel_exp_not:w { {#3} {#2} } }
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_tl:Nnn #1
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_tl_stop:w
        \s__sort_merge_exp_stop
        \__sort_if_mark:w \s__sort_merge_exp_stop \__sort_merge_exp_bottom_up_tl_mark:w
        \s__sort_merge_exp_mark
        #1
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_tl:Nnn #2
      {
        \s__sort_merge_exp_mark \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_tl_mark:w
        \s__sort_merge_exp_mark
        #1#2#3
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_tl:Nnn #4
        \s__sort_merge_exp_stop
      {
        { \__kernel_exp_not:w {#2} }
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \s__sort_merge_exp_mark \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_after_bottom_up_tl:Nw
        #1#2 \s__sort_merge_exp_list #3
      {
        \__sort_if_stop:w #3 \__sort_merge_exp_after_stop:w \s__sort_merge_exp_stop
        \__sort_if_mark:w #3 \__sort_merge_exp_after_mark_tl:w \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_after_merge_tl:Nw \exp_after:wN #1
        \tex_expanded:D { \if_false: } \fi:
          \__sort_merge_exp_merge:Nw #1#2 \s__sort_merge_exp_list {#3}
      }
    \cs_new:Npn \__sort_merge_exp_after_merge_tl:Nw
        #1#2 \s__sort_merge_exp_list #3
      {
        \__sort_if_mark:w #3 \__sort_merge_exp_after_mark_tl:w \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_after_merge_tl:Nw \exp_after:wN #1
        \tex_expanded:D { \if_false: } \fi:
          \__sort_merge_exp_merge:Nw #1#2 \s__sort_merge_exp_list {#3}
      }
    \cs_new:Npn \__sort_merge_exp_after_stop:w
        \s__sort_merge_exp_stop
        \__sort_if_mark:w \s__sort_merge_exp_stop #1 \s__sort_merge_exp_mark
        \exp_after:wN #2 \exp_after:wN #3
        \tex_expanded:D #4 \fi:
        \__sort_merge_exp_merge:Nw #5 \s__sort_merge_exp_mark \s__sort_merge_exp_list #6
        \s__sort_merge_exp_list
      {{}}
    \cs_new:Npn \__sort_merge_exp_after_mark_tl:w
        \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_after_merge_tl:Nw \exp_after:wN #1
        \tex_expanded:D #2 \fi:
        \__sort_merge_exp_merge:Nw #3 \s__sort_merge_exp_tail \s__sort_merge_exp_list
        #4 \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      { \exp_after:wN { \use_none:n #3 } }
    \cs_new:Npn \__sort_merge_exp_merge:Nw #1#2#3 \s__sort_merge_exp_list #4
      {
        #1 {#2} {#4}
          \__sort_merge_exp_merge_i:Nnnn
          \__sort_merge_exp_merge_ii:Nw
        #1 {#2} {#4}
        #3 \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_merge_i:Nnnn #1#2#3#4
      {
        { \__kernel_exp_not:w {#2} }
        \__sort_if_tail:w #4 \__sort_merge_exp_merge_i_tail:w \s__sort_merge_exp_tail
        #1 {#4} {#3}
          \__sort_merge_exp_merge_i:Nnnn
          \__sort_merge_exp_merge_ii:Nw
        #1 {#4} {#3}
      }
    \cs_new:Npn \__sort_merge_exp_merge_i_tail:w
        \s__sort_merge_exp_tail
        #1#2#3 \__sort_merge_exp_merge_i:Nnnn \__sort_merge_exp_merge_ii:Nw
        #4
        \s__sort_merge_exp_list
        #5 \s__sort_merge_exp_list
      {
        \__kernel_exp_not:w { {#3} #5 } \s__sort_merge_exp_list
        \__sort_merge_exp_merge_next:Nw #1
      }
    \cs_new:Npn \__sort_merge_exp_merge_ii:Nw #1#2#3#4 \s__sort_merge_exp_list #5
      {
        { \__kernel_exp_not:w {#3} }
        \__sort_if_tail:w #5 \__sort_merge_exp_merge_ii_tail:w \s__sort_merge_exp_tail
        #1 {#2} {#5}
          \__sort_merge_exp_merge_i:Nnnn
          \__sort_merge_exp_merge_ii:Nw
        #1 {#2} {#5}
        #4 \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_merge_ii_tail:w
        \s__sort_merge_exp_tail
        #1#2#3 \__sort_merge_exp_merge_i:Nnnn \__sort_merge_exp_merge_ii:Nw
        #4#5#6
        #7 \s__sort_merge_exp_list
        \s__sort_merge_exp_list
      {
        \__kernel_exp_not:w { {#2} #7 } \s__sort_merge_exp_list
        \__sort_merge_exp_merge_next:Nw #1
      }
    \cs_new:Npn \__sort_merge_exp_merge_next:Nw #1#2#3 \s__sort_merge_exp_list #4
      {
        \__sort_if_stop:w #4 \__sort_merge_exp_merge_next_stop:w \s__sort_merge_exp_stop
        \__sort_if_mark:w #4 \__sort_merge_exp_merge_next_mark:w \s__sort_merge_exp_mark
        #1 {#2} {#4}
          \__sort_merge_exp_merge_i:Nnnn
          \__sort_merge_exp_merge_ii:Nw
        #1 {#2} {#4}
        #3 \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_merge_next_stop:w
        \s__sort_merge_exp_stop
        \__sort_if_mark:w \s__sort_merge_exp_stop \__sort_merge_exp_merge_next_mark:w
        \s__sort_merge_exp_mark
        #1 \__sort_merge_exp_merge_i:Nnnn \__sort_merge_exp_merge_ii:Nw
        #2 \s__sort_merge_exp_list
      {
        \if_false: { \fi: }
        \s__sort_merge_exp_mark \s__sort_merge_exp_list \s__sort_merge_exp_stop
      }
    \cs_new:Npn \__sort_merge_exp_merge_next_mark:w
        \s__sort_merge_exp_mark
        #1#2#3 \__sort_merge_exp_merge_i:Nnnn \__sort_merge_exp_merge_ii:Nw
        #4#5#6
        #7 \s__sort_merge_exp_list
      {
        \if_false: { \fi: }
        {#2} #7 \s__sort_merge_exp_list \s__sort_merge_exp_mark
      }
    \cs_new:Npn \clist_sort:nN #1#2
      {
        \__kernel_exp_not:w
          \exp_after:wN \__sort_merge_exp_after_bottom_up_clist:Nw \exp_after:wN #2
          \tex_expanded:D
            {
              \__sort_merge_exp_bottom_up_clist_n:Nw
                #2 \prg_do_nothing: #1 , \s__sort_merge_exp_mark ,
              \s__sort_merge_exp_stop
            }
      }
    \cs_new:Npn \__sort_merge_exp_after_bottom_up_clist:Nw
        #1#2 \s__sort_merge_exp_list #3
      {
        \__sort_if_stop:w #3 \__sort_merge_exp_after_stop:w \s__sort_merge_exp_stop
        \__sort_if_mark:w #3 \__sort_merge_exp_after_mark_clist:w \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_after_merge_clist:Nw \exp_after:wN #1
        \tex_expanded:D { \if_false: } \fi:
          \__sort_merge_exp_merge:Nw #1#2 \s__sort_merge_exp_list {#3}
      }
    \cs_new:Npn \__sort_merge_exp_after_merge_clist:Nw
        #1#2 \s__sort_merge_exp_list #3
      {
        \__sort_if_mark:w #3 \__sort_merge_exp_after_mark_clist:w \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_after_merge_clist:Nw \exp_after:wN #1
        \tex_expanded:D { \if_false: } \fi:
          \__sort_merge_exp_merge:Nw #1#2 \s__sort_merge_exp_list {#3}
      }
    \cs_new:Npn \__sort_merge_exp_after_mark_clist:w
        \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_after_merge_clist:Nw \exp_after:wN #1
        \tex_expanded:D #2 \fi:
        \__sort_merge_exp_merge:Nw #3#4 \s__sort_merge_exp_list
        #5 \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      { \tex_expanded:D { { \__sort_merge_exp_to_clist:n #4 } } }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n:Nw #1#2,
      {
        \__sort_if_mark:w
          #2 \__sort_merge_exp_bottom_up_clist_n_mark_i:w \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_bottom_up_clist_n:n \exp_after:wN {#2}
        #1 \prg_do_nothing:
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n:n #1
      {
        \tl_if_blank:nTF {#1}
          { \__sort_merge_exp_bottom_up_clist_n:Nw }
          {
            \tl_trim_spaces_apply:nN {#1} \__sort_merge_exp_bottom_up_clist_n_auxi:n
          }
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n_auxi:n #1
      { \__sort_merge_exp_bottom_up_clist_n:wNw #1 , }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n:wNw #1,
      { \__sort_merge_exp_bottom_up_clist_n:nNw {#1} }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n:nNw #1#2#3,
      {
        \__sort_if_mark:w
          #3 \__sort_merge_exp_bottom_up_clist_n_mark_ii:w \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_bottom_up_clist_n_auxii:n \exp_after:wN
          {#3} {#1} #2
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n_auxii:n #1
      {
        \tl_if_blank:nT {#1} \__sort_merge_exp_bottom_up_clist_n:w
        \tl_trim_spaces_apply:nN {#1} \__sort_merge_exp_bottom_up_clist_n_auxiii:n
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n_auxiii:n #1
      { \__sort_merge_exp_bottom_up_clist_n:wnN #1 , }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n:wnN #1,#2#3
      {
        #3 {#2} {#1}
          { \__kernel_exp_not:w { {#2} {#1} } }
          { \__kernel_exp_not:w { {#1} {#2} } }
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_clist_n:Nw #3 \prg_do_nothing:
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n:w
        \tl_trim_spaces_apply:nN #1 \__sort_merge_exp_bottom_up_clist_n_auxiii:n
        #2#3
      { \__sort_merge_exp_bottom_up_clist_n:nNw {#2} #3 \prg_do_nothing: }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n_mark_i:w
        \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_bottom_up_clist_n:n \exp_after:wN #1
        \prg_do_nothing:
        \s__sort_merge_exp_stop
      {
        \s__sort_merge_exp_mark \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_n_mark_ii:w
        \s__sort_merge_exp_mark
        \exp_after:wN \__sort_merge_exp_bottom_up_clist_n_auxii:n \exp_after:wN
        #1#2#3
        \s__sort_merge_exp_stop
      {
        { \__kernel_exp_not:w {#2} }
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \s__sort_merge_exp_mark \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_N:Nw #1#2,#3,
      {
        \__sort_if_stop:w
          #3 \__sort_merge_exp_bottom_up_clist_N_stop:w \s__sort_merge_exp_stop
        \__sort_if_mark:w
          #3 \__sort_merge_exp_bottom_up_clist_N_mark:w \s__sort_merge_exp_mark
        #1 {#2} {#3}
          { \__kernel_exp_not:w { {#2} {#3} } }
          { \__kernel_exp_not:w { {#3} {#2} } }
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_clist_N:Nw #1
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_N_stop:w
        \s__sort_merge_exp_stop
        \__sort_if_mark:w \s__sort_merge_exp_stop
        \__sort_merge_exp_bottom_up_clist_N_mark:w \s__sort_merge_exp_mark
        #1
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_clist_N:Nw #2
      {
        \s__sort_merge_exp_mark \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_bottom_up_clist_N_mark:w
        \s__sort_merge_exp_mark
        #1#2#3
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \__sort_merge_exp_bottom_up_clist_N:Nw #4
        \s__sort_merge_exp_stop ,
      {
        { \__kernel_exp_not:w {#2} }
        \s__sort_merge_exp_tail \s__sort_merge_exp_list
        \s__sort_merge_exp_mark \s__sort_merge_exp_list
        \s__sort_merge_exp_stop \s__sort_merge_exp_list
      }
    \cs_new:Npn \__sort_merge_exp_to_clist:n #1
      {
        \__clist_if_wrap:nTF {#1}
          { { \__kernel_exp_not:w {#1} } }
          { \__kernel_exp_not:w {#1} }
        \__sort_merge_exp_to_clist_next:n
      }
    \cs_new:Npn \__sort_merge_exp_to_clist_next:n #1
      {
        \__sort_if_tail:w #1 \__sort_merge_exp_to_clist_done:w \s__sort_merge_exp_tail
        ,
        \__clist_if_wrap:nTF {#1}
          { { \__kernel_exp_not:w {#1} } }
          { \__kernel_exp_not:w {#1} }
        \__sort_merge_exp_to_clist_next:n
      }
    \cs_new:Npn \__sort_merge_exp_to_clist_done:w
        \s__sort_merge_exp_tail
        ,
        \__clist_if_wrap:nTF #1
        \__sort_merge_exp_to_clist_next:n
      {}

% actual new code for this 
\cs_new_eq:NN \__namesort_strcmp:ee \tex_strcmp:D
\prg_new_conditional:Npnn \namesort_sort:nn #1#2 {TF}
  {
    \if:w
        1
        \__namesort_strcmp:ee
          { \__namesort_sort:w #1 \q_stop }
          { \__namesort_sort:w #2 \q_stop }
      % to get the speed optimization of `\prg_new_conditional:Npnn`
      \__namesort_turn_false:w
    \fi:
    \if_true:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
\cs_new:Npn \__namesort_turn_false:w \fi: \if_true: { \fi: \if_false: }
\cs_new:Npn \__namesort_sort:w #1~#2 \q_stop { \tl_to_str:n { #2~#1 } }
\NewExpandableDocumentCommand \namesort { m m }
  { \clist_use:en { \clist_sort:nN {#2} \namesort_sort:nnTF } {#1} }
\cs_generate_variant:Nn \clist_use:nn { e }
\ExplSyntaxOff

\newcommand\solveSoC
  {%
    \ducksay[vpad=1]
      {%
        The sorted order is:\\\phantom{mm}%
        \namesort{\\\phantom{mm}}
          {%
            Scrooge McDuck,
            Huey Duck,
            Dewey Duck,
            Louie Duck,
            Webby Vanderquack,
            Launchpad McQuack,
            Bentina Beakley,
            Donald Duck,
            Della Duck,
          }%
      }%
  }

\begin{document}
\solveSoC
\end{document}
```

![soc8-sorting-1.png](/image?hash=8913eabfb7d5f4e269c1b379f65704c7928a499553c1c402dde7af2f8ecec0bd)

Enter question or answer id or url (and optionally further answer ids/urls from the same question) from

Separate each id/url with a space. No need to list your own answers; they will be imported automatically.