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)