SciTE sort selection tool : numbers with leading spaces are not sorted as expected

559 Views Asked by At

The SciTE editor comes with embedded Lua scripting engine having access to the text buffer of the editor. This makes it possible to extend SciTE's functionality with tools programmed in Lua and started from the Tools Menu. One such tool available from here:

http://lua-users.org/wiki/SciteSortSelection

is a tool for sorting of selected lines in alphabetical order. Annoying for me was/is that it doesn't sort lines containing numbers in their numerical order but like this:

    1
  111
    2
  222
    3
  333

where I would rather expect:

    1
    2
    3
  111
  222
  333

Google and Co. are not of much help here as there is to my knowledge no solution to this problem available yet online. It is also not that easy to find and deeply understand the Lua documentation for table.sort(). So the question for a knowledgeable Lua programmer would be, what is the best way to patch the existing Lua script code, so that numbers (and also lines with text in case of leading spaces) become sorted as expected and the Lua code for this task runs so fast, that even sorting of huge files (50 MByte and more) won't take much time?

2

There are 2 best solutions below

0
Piglet On BEST ANSWER

Your expectation is wrong. You said the algorithm is supposed to sort the texts alphabetically and that is exactly what it does.

For Lua "11" is smaller than "2". I think you would agree that "aa" should come befor "b" which is pretty much the same thing.

If you want to change how texts are sorted you have to provide your own function.

The Lua reference manual says:

table.sort (list [, comp])

Sorts list elements in a given order, in-place, from list[1] to list[#list]. If comp is given, then it must be a function that receives two list elements and returns true when the first element must come before the second in the final order (so that, after the sort, i < j implies not comp(list[j],list[i])). If comp is not given, then the standard Lua operator < is used instead.

Note that the comp function must define a strict partial order over the elements in the list; that is, it must be asymmetric and transitive. Otherwise, no valid sort may be possible.

The sort algorithm is not stable: elements considered equal by the given order may have their relative positions changed by the sort.

So you are free to implement your own comp function to change the sorting.

By default table.sort(list) sort list in ascending order. To make it sort in descending order you call:

table.sort(list, function(a,b) return a > b end)

If you want to treat numbers differently you can do something like this:

t = {"111", "11", "3", "2", "a", "b"}

local function myCompare(a,b)
    local a_number = tonumber(a)
    local b_number = tonumber(b)
    if a_number and b_number then
       return a_number < b_number
    end
end

table.sort(t, myCompare)

for i,v in ipairs(t) do
    print(v)
end

which would give you the output

2
3
11
111
a
b

Of course this is just a quick and simple example. A nicer implementation is up to you.

1
Claudio On

Below what I finally came up with myself. It's sure a quick and dirty solution which slows the already slow

( compared to jEdit [Plugins]->[Text Tools]->[Sort Lines] or to bash command line 'sort -g' )

process of sorting huge buffers of text lines, but it is at least there for use and works as expected. For the sake of completeness here the entire section of code currently present in my Lua Startup Script for SciTE:

-- =============================================================================
-- Sort Selected Lines (available in MENU -> Tools):
-- -----------------------------------------------------------
-- Specify in .SciTEUser.properties:
--     command.name.2.*=# Sort Selected Lines    '
--     command.subsystem.2.*=3
--     command.mode.2.*=savebefore:no
--     command.2.*=SortSelectedLines
--     # command.shortcut.2.*=Ctrl+2 # Ctrl+2 is DEFAULT for command.2.*

function lines(str)
  local t = {}
  local i, lstr = 1, #str
  while i <= lstr do
    local x, y = string.find(str, "\r?\n", i)
    if x then t[#t + 1] = string.sub(str, i, x - 1)
    else break
    end
    i = y + 1
  end
  if i <= lstr then t[#t + 1] = string.sub(str, i) end
  return t
end

-- It was an annoying for me that using table.sort(buffer) in Lua  
-- didn't sort numbers with leading spaces in their numerical order. 
-- Using following comparison function helps to avoid that problem: 
function compare(a,b)
  return a:gsub(" ", "0") < b:gsub(" ", "0")
-- If 'compare' is not used ( table.sort(buf) )
-- Lua uses implicit for sorting (see Lua tutorial): 
--   return a < b
-- so changing the provided return statement to this above
-- would be enough to restore sorting to how it was before 
end

function SortSelectedLines()
  local sel = editor:GetSelText()
  if #sel == 0 then return end
  local eol = string.match(sel, "\n$")
  local buf = lines(sel)
  table.sort(buf, compare)
--table.foreach (buf, print) --used for debugging
  local out = table.concat(buf, "\n")
  if eol then out = out.."\n" end
  editor:ReplaceSel(out)
end

--  ---------
-- :Sort Selected Lines
-- -----------------------------------------------------------------------------