table.sort
As a Roblox developer, it is currently inefficient to sort a table alphabetically (using table.sort(t) as intended). It results in an illogical manner. This is also occurs in the Explorer. It is intended to sort all objects in it alphabetically, including numbers.
However because of how it’s handled, things like this happen:
Which should be
It would make much more sense if this code:
local t = {"1","5","3","4","7","2","6","10","8","9"}
table.sort(t)
for _ , number in pairs (t) do
print(number)
end
But that’s how a proper alphabetical sort works. What you’re asking for is a more contextual-based sort. Windows started doing this in either Vista or Windows 7, but before had the same issue. (e.g. a file named “Image_10” would show before “Image_2”).
In an alphabetical sort, the algorithm looks at a character-per-character basis; it doesn’t consider the whole context (e.g. finding a multi-digit number).
Changing the default behavior of table.sort could result in game-breaking issues.
In the format function, an i or d represent an integer. The i by itself as the 2nd parameter is from the for loop. Check out the Format String page for more info.
I did some more research on this. Apparently what you’re looking for is something called a “Natural Sort”. After some googling, I found a Lua implementation. I reformatted it just a bit:
function NaturalSort(tbl)
local function Convert(s)
local res, dot = "", ""
for n, m, c in tostring(s):gmatch("(0*(%d*))(.?)") do
if (n == "") then
dot, c = "", dot .. c
else
res = res .. (dot == "" and ("%03d%s"):format(#m, m) or "." .. n)
dot, c = c:match("(%.?)(.*)")
end
res = res .. c:gsub(".", "\0%0")
end
return res
end
table.sort(tbl, function(a, b)
local ca, cb = Convert(a), Convert(b)
return (ca < cb or ca == cb and a < b)
end)
return tbl
end
Example:
local strs = {"Hi10", "Hi2", "Hi1", "Hi32", "Hi5", "Hi3", "Hi0"}
NaturalSort(strs)
for i,s in pairs(strs) do print(i,s) end
And here is the blog post that gave me lots of good info on this subject.
Here’s what I went with. It works by continually splitting each string by its first sequence of digits.
local function NaturalSort(a, b)
-- Split string by first sequence of digits.
local function findNum(s)
local i, j, n = s:find("(%d+)")
if not i then
return s, 0, ""
end
-- Return prefix, number, suffix
return s:sub(1, i-1), tonumber(n), s:sub(j+1)
end
local apfx, anum, bpfx, bnum
while true do
if b == "" then
return false -- b < a
end
if a == "" then
return true -- a < b
end
apfx, anum, a = findNum(a)
bpfx, bnum, b = findNum(b)
if apfx ~= bpfx then
return apfx < bpfx
end
if anum ~= bnum then
return anum < bnum
end
end
end
local t = {"foo10","bar29","foo11","bar2","foo1","foo7","bar25","foo12","foo9"}
table.sort(t, NaturalSort)
for i = 1,#t do print(t[i]) end
Also, the concept of sort order seems to be referred to as “collation”, if you want to look up more.