[Tex/LaTex] Sorting a comma-separated list with LaTeX

comma-separated listexpl3sorting

Is there a simple way to sort a comma separated list alphabetically in LaTeX? I tried to write a macro (\sortlist{World, Hello} → "Hello, World") using the l3sort documentation example but it completely failed.

\ExplSyntaxOn
\def\@sortlist{}
\newcommand{\sortlist}[1]{
    \clist_set:Nn \l_foo_clist {#1}
    \clist_sort:Nn \l_foo_clist{
      \int_compare:nNnTF { ##1 } > { ##2 }
      { \sort_return_swapped: }
      { \sort_return_same: }
    }
    \def\@sortlist{\l_foo_clist}
}
\ExplSyntaxOff

Is there something like \str_compare to make a string comparison or can l3sort actually only be used for numerical values?

Edit:
How to process a comma separated list? wasn't really helpful for me or rather I wasn't able to solve my problem thereby.

Best Answer

This seems to work, with no packages. EDITED to solve the upper/lower-case problem.

EDIT: Resolved problem when a comparison ran out of letters prior to resolving the order, for example, wash, washer.

See ADDENDUM for handling (after a fashion) diacritics.

\documentclass{article}
\def\listterminator{;}
\makeatletter
\newcommand\alphabubblesort[1]{\def\presorted{}\def\sortedlist{}%
  \sortlist#1,\listterminator,\relax}
\def\sortlist#1#2,#3#4,#5\relax{%
  \if\listterminator#3#4\relax%
    \edef\sortedlist{\sortedlist#1#2}%
  \else
    \ifnum\the\lccode`#1<\the\lccode`#3\relax%
      \edef\sortedlist{\sortedlist\presorted#1#2, }%
      \expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
      \def\presorted{}%
      \expandafter\sortlist\svfirst,#5\relax%
    \else%
      \ifnum`#1=`#3\relax%
        \ifx\relax#2\relax%
          \edef\sortedlist{\sortedlist\presorted#1#2, }%
          \expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
          \def\presorted{}%
          \expandafter\sortlist\svfirst,#5\relax%          
        \else%
          \ifx\relax#4\relax%
            \edef\sortedlist{\sortedlist\presorted#3#4, }%
            \expandafter\def\expandafter\svfirst\expandafter{\presorted#1#2}%
            \def\presorted{}%
            \expandafter\sortlist\svfirst,#5\relax%
          \else        
            \g@addto@macro\presorted{#1}%
            \sortlist#2,#4,#5\relax%
          \fi%
        \fi%
      \else%
        \let\tmp\sortedlist%
        \def\sortedlist{}%
        \expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
        \expandafter\def\expandafter\svsecond\expandafter{\presorted#1#2}%
        \def\presorted{}%
        \expandafter\expandafter\expandafter\expandafter\expandafter%
        \expandafter\expandafter\sortlist\expandafter\expandafter%
        \expandafter\tmp\expandafter\svfirst\expandafter,\svsecond,#5\relax%
      \fi%
    \fi%
  \fi%
}
\makeatother
\begin{document}
\alphabubblesort{book, boot, boat,toad,attic,wish,wash,wasn't,Cat ,Xylophone}
\sortedlist\par

\alphabubblesort{book, washer, boot, boat,toad,attic,wish,wash,wasn't,Cat ,Xylophone}
\sortedlist\par
\end{document}

enter image description here

ADDENDUM

Here's a version that can handle diacritics, in the sense that they do not break the algorithm. This was accomplished by changing the \edefs in the above algorithm to appropriately expanded \defs.

However, diacritics here will always precede all non-diacritic letters in the sort. While maybe not the ideal behavior, it may still be useful.

\documentclass{article}
\def\listterminator{;}
\makeatletter
\newcommand\alphabubblesort[1]{\def\presorted{}\def\sortedlist{}%
  \sortlist#1,\listterminator,\relax}
\def\sortlist#1#2,#3#4,#5\relax{%
  \if\listterminator#3#4\relax%
    \expandafter\def\expandafter\sortedlist\expandafter{\sortedlist#1#2}%
  \else
    \ifnum\the\lccode`#1<\the\lccode`#3\relax%
      \expandafter\expandafter\expandafter\def\expandafter\expandafter%
      \expandafter\sortedlist\expandafter\expandafter\expandafter{%
      \expandafter\sortedlist\presorted#1#2, }%
      \expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
      \def\presorted{}%
      \expandafter\sortlist\svfirst,#5\relax%
    \else%
      \ifnum`#1=`#3\relax%
        \ifx\relax#2\relax%
          \expandafter\expandafter\expandafter\def\expandafter\expandafter%
          \expandafter\sortedlist\expandafter\expandafter\expandafter{%
          \expandafter\sortedlist\presorted#1#2, }%
          \expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
          \def\presorted{}%
          \expandafter\sortlist\svfirst,#5\relax%          
        \else%
          \ifx\relax#4\relax%
            \expandafter\expandafter\expandafter\def\expandafter\expandafter%
            \expandafter\sortedlist\expandafter\expandafter\expandafter{%
            \expandafter\sortedlist\presorted#3#4, }%
            \expandafter\def\expandafter\svfirst\expandafter{\presorted#1#2}%
            \def\presorted{}%
            \expandafter\sortlist\svfirst,#5\relax%
          \else        
            \g@addto@macro\presorted{#1}%
            \sortlist#2,#4,#5\relax%
          \fi%
        \fi%
      \else%
        \let\tmp\sortedlist%
        \def\sortedlist{}%
        \expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
        \expandafter\def\expandafter\svsecond\expandafter{\presorted#1#2}%
        \def\presorted{}%
        \expandafter\expandafter\expandafter\expandafter\expandafter%
        \expandafter\expandafter\sortlist\expandafter\expandafter%
        \expandafter\tmp\expandafter\svfirst\expandafter,\svsecond,#5\relax%
      \fi%
    \fi%
  \fi%
}
\makeatother
\begin{document}
\alphabubblesort{book, washer, w\"asher, boot, boat,toad,attic,wish,wash,wasn't,
  Cat, Xylophone, w\"ash, edifice, \'edifice, w\"asherei}
\sortedlist\par
\end{document}

enter image description here

The algorithm was based on my \bubblesort macro here: Using LaTeX to compact a list of numbers