Alphabetical Sorting Of A Sequence Of Names


Answer :

Bubble sorter, which I adapt from my modification to David's answer to my question at Trying to eliminate stack overflow during recursion.

The \sortlist macro is the bubble sorter (from the referenced answer, but with and rather than , as the list seperator). However, it leaves the result in the form of Last Name, First and ....

I had to add the \rework macro to make it First Last Name and employ \whichsep to choose whether a , or and should be inserted between names, depending on their placement in the list.

No packages required!

\documentclass[10pt]{article} \newcommand\alphabubblesort[1]{\def\sortedlist{}%   \expandafter\sortlist#1 and \cr and \relax   \expandafter\rework\sortedlist and \relax} \def\sortlist#1and #2and #3\relax{%   \let\next\relax   \ifx\cr#2\relax%     \edef\sortedlist{\sortedlist#1}%   \else     \picknext#1!and #2!\relax%     \if F\flipflop%       \edef\sortedlist{\sortedlist#1and }%       \def\next{\sortlist#2and #3\relax}%     \else%       \let\tmp\sortedlist%       \def\sortedlist{}%       \def\next{\expandafter\sortlist\tmp#2and #1and #3\relax}%     \fi%   \fi% \next } \def\picknext#1#2and #3#4\relax{%   \ifnum\the\lccode`#1<\the\lccode`#3\relax     \xdef\flipflop{F}%   \else%     \ifnum\the\lccode`#1>\the\lccode`#3\relax%       \xdef\flipflop{T}%     \else%       \ZZfifi{\picknext#2!and #4!\relax}%     \fi%   \fi% } \def\ZZfifi#1\fi\fi{\fi\fi#1} \def\rework#1, #2and #3\relax{#2#1\ifx\relax#3\relax\else   \whichsep#3,\relax\rework#3\relax\fi} \def\whichsep#1,#2,#3\relax{\ifx\relax#3\relax\ and \else, \fi} \begin{document} \def\mydata{% Gauss, Carl Friedrich and Riemann, Bernhard and Euler, Leonhard} \alphabubblesort{\mydata}  I wish to thank  \alphabubblesort{% Gauss, Carl Friedrich and  Riemann, Bernhard and  Euler, Leonhard and  Bach, Carl Philipp Emanuel and  Dumbledore, Albus Percival Wulfric Brian and  Granger, Hermione Jean and  Scott Thomas, Kristin and  Van Gogh, Vincent and  Sartre, Jean-Paul and  Toulouse-Lautrec, Henri de} for their valuable comments and incisive critiques. \end{document} 

enter image description here


Is this what you want? I define some ordering rule:

  • aA: [a-zA-Z]
  • raA: [Z-Az-a]
  • Aa: [A-Za-z]
  • rAa: [z-aZ-A]

You can also define your owner ordering rule.

enter image description here

\documentclass{article} \usepackage{xparse} \ExplSyntaxOn  \tl_new:N \l__seq_sep_tl \seq_new:N \l__alph_seq \seq_new:N \l__Alph_seq \seq_new:N \l__Alphalpa_seq \seq_new:N \l__alphAlph_seq \seq_new:N \l__ralph_seq \seq_new:N \l__rAlph_seq \seq_new:N \l__rAlphalpa_seq \seq_new:N \l__ralphAlph_seq \seq_new:N \l__result_seq \seq_new:N \l__custom_order_seq \bool_new:N \l__if_less_bool \prop_new:N \l__order_prop  \seq_set_from_clist:Nn \l__alph_seq   { a,b,c,d,e,f,g,h,i,g,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z } \seq_set_from_clist:Nn \l__Alph_seq   { A,B,C,D,E,F,G,H,I,G,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z } \seq_set_from_clist:Nn \l__Alphalph_seq   {     A,B,C,D,E,F,G,H,I,G,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,     a,b,c,d,e,f,g,h,i,g,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z   } \seq_set_from_clist:Nn \l__alphAlph_seq   {     a,b,c,d,e,f,g,h,i,g,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,     A,B,C,D,E,F,G,H,I,G,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z   } \seq_set_eq:NN \l__ralph_seq \l__alph_seq \seq_set_eq:NN \l__rAlph_seq \l__Alph_seq \seq_set_eq:NN \l__ralphAlph_seq \l__alphAlph_seq \seq_set_eq:NN \l__rAlphalph_seq \l__Alphalph_seq \seq_reverse:N \l__ralph_seq \seq_reverse:N \l__rAlph_seq \seq_reverse:N \l__ralphAlph_seq \seq_reverse:N \l__rAlphalph_seq \seq_set_eq:NN \l__custom_order_seq \l__Alphalph_seq  \prop_set_from_keyval:Nn \l__order_prop   {     a = alph,     A = Alph,     aA = alphAlph,     Aa = Alphalph,     ra = ralph,     rA = rAlph,     raA = ralphAlph,     rAa = rAlphalph,   }  \keys_define:nn { sort }   {     order .code:n = { \set_order_from_option:n { #1 } },     sep .tl_set:N = \l__seq_sep_tl,   }  \cs_new_protected:Nn \set_sort_order_from_seq:N   {     \int_zero:N \l_tmpa_int     \seq_remove_duplicates:N #1     \seq_map_inline:Nn #1       {         \int_incr:N \l_tmpa_int         \int_if_exist:cF { g__sort_##1 }           {             \int_new:c { g__sort_##1 }           }         \int_gset_eq:cN { g__sort_##1 } \l_tmpa_int       }   }  \prg_new_protected_conditional:Nnn \str_if_less:nn { T, F, TF }   {     \int_set:Nn \l_tmpa_int       { \str_count_ignore_spaces:n { #1 } }     \int_set:Nn \l_tmpb_int       { \str_count_ignore_spaces:n { #2 } }     \int_compare:nTF { \l_tmpa_int < \l_tmpb_int }       { \bool_set_true:N \l__if_less_bool }       { \bool_set_false:N \l__if_less_bool }     \int_step_inline:nn { \int_min:nn { \l_tmpa_int } { \l_tmpb_int } }       {         \int_set_eq:Nc \l_tmpa_int           { g__sort_\str_item:nn { #1 } { ##1 } }         \int_set_eq:Nc \l_tmpb_int           { g__sort_\str_item:nn { #2 } { ##1 } }         \int_compare:nF { \l_tmpa_int = \l_tmpb_int }           {             \int_compare:nTF { \l_tmpa_int < \l_tmpb_int }               { \bool_set_true:N \l__if_less_bool }               { \bool_set_false:N \l__if_less_bool }             \prg_break:           }       }     \bool_if:NTF \l__if_less_bool       { \prg_return_true: }       { \prg_return_false: }   }  % #1 seq to be sorted #2 predefined order seq \cs_new_protected:Nn \seq_sort_by_order:NN   {     \set_sort_order_from_seq:N #2     \seq_sort:Nn #1       {         \str_if_less:nnTF { ##1 } { ##2 }           { \sort_return_same: }           { \sort_return_swapped: }       }   }   \cs_generate_variant:Nn \seq_set_split:Nnn { Nxo } \cs_new_protected:Nn \sort_custom_seq:nn   {     \keys_set:nn { sort }       {         sep = {,},         #1       }     \seq_set_split:Nxo \l__result_seq { \l__seq_sep_tl } { #2 }     \seq_sort_by_order:NN \l__result_seq \l__custom_order_seq   }  % #1 seq handle function #2 options #3 list \cs_new_protected:Nn \sort_custom_seq:Nnn   {     \sort_custom_seq:nn { #2 } { #3 }     #1 \l__result_seq   }  \cs_new_protected:Nn \my_transpose:N   {     \seq_clear_new:N \l__new_seq     \seq_map_inline:Nn #1       {         \seq_clear_new:N \l__item_seq         \seq_set_split:Nnn \l__item_seq { , } { ##1 }         \seq_reverse:N \l__item_seq         \seq_put_right:Nx \l__new_seq { \seq_use:Nn \l__item_seq { ~ } }       }     \seq_set_eq:NN #1 \l__new_seq   }  \cs_new_protected:Nn \set_order_from_seq:nn   {     \seq_set_split:Nnn \l__custom_order_seq { #1 } { #2 }   }  \cs_new_protected:Nn \set_order_from_str:n   {     \str_set:Nn \l_tmpa_str { #1 }     \seq_clear:N \l__custom_order_seq     \str_map_inline:Nn \l_tmpa_str       {         \seq_put_right:Nn \l__custom_order_seq { ##1 }       }   }  \cs_new_protected:Nn \set_order_from_option:n   {     \prop_if_in:NnTF \l__order_prop { #1 }       {         \seq_set_eq:Nc \l__custom_order_seq           { l__\prop_item:Nn \l__order_prop { #1 }_seq }       }       {         \set_order_from_str:n { #1 }       }   }  \NewDocumentCommand { \setorder } { o m }   {     \IfNoValueTF { #1 }       { \set_order_from_str:n { #2 } }       { \set_order_from_seq:nn { #1 } { #2 } }   }  \NewDocumentCommand { \mysorted } { O{} +m }   {     \sort_custom_seq:Nnn \my_transpose:N { #1 } { #2 }     \seq_use:Nnnn \l__result_seq { ~and~ } { ,~ } { ~and~ }   }  \NewDocumentCommand { \sorted } { m +m }   {     \sort_custom_seq:nn { order = #1 } { #2 }     \makebox[4cm][l]{\bfseries Order:~#1}     \seq_map_inline:Nn \l__result_seq       {         \makebox[1.2cm][l]{##1}       }   }  \ExplSyntaxOff  \begin{document} \mysorted[sep=and]{Gauss, Carl Friedrich and Riemann, Bernhard and Euler, Leonhard}  \def\test{app, band, apple, Apple, App} \sorted{aA}{\test}  \sorted{raA}{\test}  \sorted{Aa}{\test}  \sorted{rAa}{\test}  \sorted{ab-+*@c}{abc, c@-, b+@, @cb, b-c} \end{document} 

Here's a LuaLaTeX based solution. It sets up a LaTeX macro called \sorted, which calls a Lua function called sorted to do most of the work. The word and is taken to be the keyword that separates persons, while , (comma) is the separator between the surname and given-name portions of a full name. Space characters and hyphen characters are allowed in both the given-name and first-name portions of a full name.

enter image description here

% !TeX program = lualatex \documentclass{article}  %% Lua-side code \usepackage{luacode} % for 'luacode' environment \begin{luacode} 
function string_to_table ( str )    local namelist = {} -- initialize the table    str:gsub ( "([^;]*)" , function ( name )          -- Strip off any leading and trailing whitespace:         name = name:gsub ( "^%s*(.-)%s*$" , "%1" )         -- Insert 'name' in 'namelist'         table.insert ( namelist , name )            end )    return namelist end  function sorted ( s )    local t    -- Change the separator keyword "and" to ";"    s = s:gsub ( "and" , ";" )    -- Convert to a Lua table:    t = string_to_table ( s )    -- Sort the table entries alphabetically:    table.sort ( t )    n = #t -- Retrieve number of entries    -- Change "Surname, FirstName" to "FirstName Surname":    for i=1,n do      t[i] = string.gsub ( t[i] , "([%a%s%-]+)%,%s?(.+)" , "%2 %1" )    end    -- Output a string, using "and" as the final separator    s = t[1]    for i = 2,n-1 do s = s .. ", " .. t[i] end    s = s .. " and " .. t[n]    tex.sprint ( s ) end  \end{luacode} 
%% LaTeX-side code: \newcommand\sorted[1]{\directlua{sorted(\luastringN{#1})}}  \begin{document} I wish to thank  \sorted{Gauss, Carl Friedrich and Riemann,Bernhard and Euler, Leonhard and  Bach, Carl Philipp Emanuel and Dumbledore,Albus Percival Wulfric Brian and  Granger, Hermione Jean and Scott Thomas, Kristin and Van Gogh, Vincent and  Sartre, Jean-Paul and Toulouse-Lautrec, Henri de} for their valuable comments and incisive critiques. \end{document} 

Comments

Popular posts from this blog

Are Regular VACUUM ANALYZE Still Recommended Under 9.1?

Can Feynman Diagrams Be Used To Represent Any Perturbation Theory?