You can try this
This is taken over from https://tex.stackexchange.com/a/273084/4686 (code 5)
% ------------------------------------------------------------------
% Expandable routine to sort strings, based on a QuickSort algorithm
% and using \pdfstrcmp.
% ------------------------------------------------------------------
% USAGE
% -----
% if using XeTeX:
% \edef\mysortedlist {\QSfull{\mylist}}, or explicit \QSfull {foo, bar, zoo}
% if using PDFLaTeX: (and inputenc for accented letters)
% \begingroup
% \subdueutfviiienc
% \global\edef\mysortedlist {\QSfull{\mylist}}
% \endgroup
\documentclass{article}
\ifdefined\XeTeXinterchartoks
\let\pdfstrcmp\strcmp
\usepackage{fontspec}
\else
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}% for PDFLaTeX
\makeatletter
% this \subdueutfviiienc allows é, à, etc... to survive as is to \edef's.
\newcommand*\subdueutfviiienc {% to be used in a group
\count@="C2
\loop
\lccode`~\count@
\lowercase{\def~####1{\noexpand~\string####1}}%
\ifnum\count@<"E0
\advance\count@\@ne
\repeat
\loop
\lccode`~\count@
\lowercase{\def~####1####2{\noexpand~\string####1\string####2}}%
\ifnum\count@<"F0
\advance\count@\@ne
\repeat
\loop
\lccode`~\count@
\lowercase{\def~####1####2####3{\noexpand~\string####1\string####2\string####3}}%
\ifnum\count@<"F4
\advance\count@\@ne
\repeat
}\makeatother
% (if inputenc is used with 8bit encoding another approach would be needed)
\fi
% CODE TAKEN FROM https://tex.stackexchange.com/a/273084/4686 (code 5)
% -------------------------------------------------------------------
\makeatletter
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
%
% use some (improbable) tokens as delimiters
\catcode`! 3
\catcode`? 3
\catcode`; 3
%
% first we check if empty list (else \qsfull@finish will not find a comma)
% we apply f-expansion to the argument to allow it to be a macro.
%
\def\QSfull #1{\expandafter\qsfull@a\romannumeral-`0#1,!,?}%
%
% first check if input has only blanks, or is empty
\def\qsfull@a #1{\ifx,#1\xintdothis\qsfull@a\fi
\ifx!#1\xintdothis\qsfull@abort\fi
\xintorthat{\qsfull@start #1}}%
\def\qsfull@abort #1?{}%
%
\def\qsfull@start {\expandafter\qsfull@finish\romannumeral0\qsfull@b,}%
\def\qsfull@finish ,#1{#1}% remove initial ,<space>
\def\qsfull@b ,#1#2,#3{\ifx?#3\xintdothis\qsfull@emptylist\fi
\ifx!#3\xintdothis\qsfull@singleton\fi
\xintorthat \qsfull@separate@a {}{}#1#2;#3}%
\def\qsfull@emptylist #1?{}%
\def\qsfull@singleton #1#2#3;!,?{, #3}%
%
\def\qsfull@separate@a #1#2#3;#4#5,%
% first pass, remove blanks in passing.
% no need to be extra efficient for that.
{%
\ifx,#4\expandafter\qsfull@valueisblank\fi
\ifx!#4\expandafter\qsfull@separate@done\fi
\if1\pdfstrcmp{#4#5}{#3}%
\expandafter\qsfull@separate@a@appendtogreater
\else\expandafter\qsfull@separate@a@appendtosmaller
\fi
#4#5?{#1}{#2}#3;%
}%
\def\qsfull@valueisblank \ifx#1\fi,#2?#3#4#5;{\qsfull@separate@a {#3}{#4}#5;#2,}%
\def\qsfull@separate@a@appendtogreater #1?#2{\qsfull@separate@a {#2, #1}}%
%
\def\qsfull@separate@a@appendtosmaller #1?#2#3{\qsfull@separate@a {#2}{#3, #1}}%
%
\def\qsfull@separate@done\if#1\fi #2?#3#4#5;?%
{%
\qsfull@c #4,!,?, #5\qsfull@c #3,!,?%
}%
% Now that the first pass is done, there are no more blank items.
% In particular here.
\def\qsfull@c ,#1#2,#3{\ifx?#3\xintdothis\qsfull@emptylist\fi
\ifx!#3\xintdothis\qsfull@singleton\fi
\xintorthat \qsfull@separate {}{}#1#2;#3}%
%
\def\qsfull@separate #1#2#3;#4#5,% blanks have already been filtered out.
{%
\ifx!#4\expandafter\qsfull@separate@done\fi
\if1\pdfstrcmp{#4#5}{#3}%
\expandafter\qsfull@separate@a@appendtogreater
\else\expandafter\qsfull@separate@a@appendtosmaller
\fi
#4#5?{#1}{#2}#3;%
}%
\def\qsfull@separate@appendtogreater #1?#2{\qsfull@separate {#2, #1}}%
\def\qsfull@separate@appendtosmaller #1?#2#3{\qsfull@separate {#2}{#3, #1}}%
%
%
\catcode`! 12
\catcode`? 12
\catcode`; 12
\makeatother
\begin{document}
\def\mylist{book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't,
Cat, Xylophone, wäsh, edifice, édifice, wäsherei}
% use either a macro \mylist or explicit list as \QSfull argument.
\ifdefined\XeTeXinterchartoks
\edef\mysortedlist{\QSfull{\mylist}}
\else
{\subdueutfviiienc\global\edef\mysortedlist{\QSfull{\mylist}}}%
\fi
\mylist\par
\medskip
becomes (notice how whitespaces are normalized in the process):
\medskip
\mysortedlist
\medskip
For some reason due to the comparison being done by \verb|\pdfstrcmp| the é
comes after a-z.
% check
\typeout{\meaning\mylist}
\typeout{\meaning\mysortedlist}
\end{document}
Here in the log output, with PDFLaTeX:
macro:->book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't, Cat, Xyl
ophone, wäsh, edifice, édifice, wäsherei
macro:->Cat, Xylophone, attic, boat, book, boot, edifice, toad, wash, washer, w
asn't, wish, wäsh, wäsher, wäsherei, édifice
and using XeLaTeX (which does not cut lines the same way):
macro:->book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't, Cat, Xylo
phone, wäsh, edifice, édifice, wäsherei
macro:->Cat, Xylophone, attic, boat, book, boot, edifice, toad, wash, washer, w
asn't, wish, wäsh, wäsher, wäsherei, édifice
The console output (again XeLaTeX) is still different, lines not cut:
macro:->book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't, Cat, Xylophone, wäsh, edifice, édifice, wäsherei
macro:->Cat, Xylophone, attic, boat, book, boot, edifice, toad, wash, washer, wasn't, wish, wäsh, wäsher, wäsherei, édifice
expl3
); it would become much more complicated if words with accents and so on are involved.