4

I want to define a command using \directlua that looks something like:

\sort{c, b, a}

and outputs a, b, c to the document.

7

In ConTeXt you do not have to reinvent the wheel.

\def\sort#1{%
  \ctxlua{
    context(
      table.concat(
        table.sorted(
          utilities.parsers.settings_to_array([[#1]]) ) , ", " ) )
}}

\starttext

\sort{c, b, a}

\stoptext

enter image description here


You can also use it with LaTeX by including the appropriate Lua headers from ConTeXt.

\documentclass{article}

\directlua{
dofile(kpse.find_file("l-lpeg.lua"))
dofile(kpse.find_file("util-sto.lua"))
dofile(kpse.find_file("util-prs.lua"))
}

\def\sort#1{%
  \directlua{\unexpanded{
    tex.sprint(
      table.concat(
        table.sorted(
          utilities.parsers.settings_to_array([[#1]]) ) , ", " ) )
}}}

\begin{document}

\sort{c, b, a}

\end{document}

The output is similar.


To omit empty entries (empty string) one has to write one additional helper function, which I could not find in ConTeXt. However, multiple empty entries side by side seem to confuse at least one of the functions, i.e. \sort{,,,,a} will produce spurious commas.

\documentclass{article}

\directlua{
dofile(kpse.find_file("l-lpeg.lua"))
dofile(kpse.find_file("l-string.lua"))
dofile(kpse.find_file("util-sto.lua"))
dofile(kpse.find_file("util-prs.lua"))

function table.strip_empty(tab)
    for k, v in pairs(tab) do
        if ( string.strip(v) == "" ) then
            table.remove(tab, k)
        end
    end
    return tab
end
}

\def\sort#1{%
  \directlua{\unexpanded{
    tex.sprint(
      table.concat(
        table.sorted(
          table.strip_empty(
            utilities.parsers.settings_to_array([[#1]]) ) ) , ", " ) )
}}}

\begin{document}

\sort{, d, , f, c, b, a, }

\end{document}
  • +1. Is there a way to modify the routines to automatically delete any leading commas (say, because the command was \sort{c, ,b, a,})? – Mico Mar 13 '17 at 13:10
  • @Mico See updated answer. Unfortunately, I could not find a remove function in ConTeXt. – Henri Menke Mar 13 '17 at 16:05
4

Here's a possible solution. The main Lua function, called dojob, converts the input string into a Lua table (via a call to an auxiliary Lua function called string_to_table), performs a simple sort, and outputs the table elements as a string, with the elements separated by "," ("comma&space"). Any leading or trailing whitespace characters and commas are discarded automtically.

The Lua function dojob may be accessed from within the body of a LaTeX document via the LaTeX macro called \sort.

Aside: Much of the Lua code in the string_to_table function was obtained from this site. (Look for the header "Method: Using only string.gsub" for more details on how the code works.) For good measure, I added a routine that removes leading and trailing whitespace from the table entries.

The sorting routine is simple. I trust your comma-separated list isn't so long as to require a more efficient sorting algorithm (e.g., QuickSort) in order to avoid getting bogged down.

enter image description here

% !TeX program = lualatex
\documentclass{article}
%% Lua-side code
\usepackage{luacode}
\begin{luacode}
function string_to_table (str)
   sep = ","
   fields = {}
   str:gsub( "([^"..sep.."]*)" .. sep , 
               function(c) 
                 c = string.gsub ( c, "^%s*(.-)%s*$", "%1" )
                 table.insert (fields, c)   
               end)
   return fields
end
function dojob ( s )
   -- append "," (if needed) to end of string
   if (string.sub(s,-1) == ",") then else s = s.."," end 
   -- convert string to table
   t = string_to_table ( s , "," )
   -- sort the table entries
   table.sort ( t , function(a,b) return a<b end)
   -- convert table to string, with items separated by commas
   s = table.concat ( t, ", " )
   -- remove any leading whitespace and comma characters, output the string
   tex.sprint ( (string.gsub( s, "^[,%s]*(.*)$", "%1" ) ) )
end
\end{luacode}

%% TeX-side code
\newcommand\sort[1]{\directlua{dojob(\luastring{#1})}}

\begin{document}
\sort{c, b, a}

\sort{Carla , Eric , Anna Michelle, Brenda  , Daniel}

\sort{Eric,Carla,Brenda,Anna Michelle,Daniel,,}

\sort{aaa,15}
\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.