luaguides

table.sort

table.sort(table [, comp])

Syntax and parameters

table.sort (table [, comp])

Sorts a sequence in place, reordering the elements of table from index 1 through #table. The function returns nothing.

local nums = { 4, 2, 1, 3 }
table.sort(nums)
-- nums is now { 1, 2, 3, 4 }

That is the whole API in four lines: pass a sequence, get it back in order. The rest of this article covers the parameter contract, the rules for custom comparators, and the gotchas that catch people in real code.

Parameters

table : A sequence: a table with integer indices from 1 to n and no nil holes. Required. The function uses the result of the length operator # to determine how many elements to sort, so a table with a __len metamethod can change the effective range.

comp : A function with the signature function (a, b): boolean. Optional. It must return true when a should come before b in the sorted order. If omitted, table.sort uses Lua’s < operator on the elements, which honors a __lt metamethod on the element’s metatable.

Return value

nil. The sort is in place; the function returns nothing. The first argument is mutated, and any other references to the same table see the reordered elements.

How it works

Under the hood, table.sort is an iterative quicksort implemented in C (ltablib.c). Average complexity is O(n log n); worst case is O(n²) on already-sorted input with a naive pivot, though the reference implementation uses median-of-three pivot selection to make the worst case harder to hit.

Three things to keep in mind that the 5.4 reference manual is quiet about:

It is not stable. Elements the comparator considers equal may be reordered. The behavior is unchanged from Lua 5.1 through 5.4. If you need a stable sort, decorate each element with its original index, sort, then strip the index back off, or fold the tie-break into the comparator.

#t is the upper bound. The sort covers indices 1 through #table. If your table is not a real sequence, the length operator can return any boundary position, and table.sort will sort an unpredictable prefix. Always start with a clean sequence.

A buggy comparator is undefined behavior. Returning true for both comp(a, b) and comp(b, a) is not a valid strict weak ordering. The C implementation does not validate, so don’t pass nonsense.

Basic examples

Ascending sort, no comparator

local nums = { 5, 2, 9, 1, 7 }
table.sort(nums)
-- nums is now { 1, 2, 5, 7, 9 }

Numbers have a built-in <, so the default comparator works. The default only knows about primitives with a built-in ordering, though: strings and numbers go through unchanged, but tables raise an error because Lua has no sensible default for {} < {}. To get a different order, or to sort non-primitive elements, pass your own comparator as the second argument.

Descending order with a custom comparator

local t = { 1, 5, 3, 9, 2 }
table.sort(t, function (a, b) return a > b end)
-- t is now { 9, 5, 3, 2, 1 }

Flip the operands to reverse the order. The comparator returns true when a should come first, so a > b puts larger values ahead.

Sort an array of tables by a field

local players = {
  { name = "Ada", score = 88 },
  { name = "Lin", score = 95 },
  { name = "Raj", score = 73 },
}

table.sort(players, function (a, b) return a.score < b.score end)

for _, p in ipairs(players) do
  print(p.name, p.score)
end

The reorder happens in place, so the original order is gone by the time table.sort returns. ipairs then walks the table through its new ordering, which is the only signal a caller gets that the sort actually took effect. The tab characters in the output come from print joining its arguments with \t (handy for column-aligned text, easy to miss when you’re skimming results).

Output:

Raj	73
Ada	88
Lin	95

Sorting tables with < directly ({} < {}) raises an error in Lua 5.4, so a comparator is the standard fix.

Custom comparators

A comparator must define a strict weak ordering: it is always false that comp(a, a) returns true, and comp(a, b) and comp(b, a) cannot both be true. Most bugs in custom comparators are violations of these rules.

Stable tie-break

When the primary key can collide, fold the tie-break into the comparator. This example sorts strings case-insensitively but falls back to case-sensitive order on equal lowercases:

local names = { "bob", "Ada", "charlie", "ada" }

table.sort(names, function (a, b)
  local la, lb = a:lower(), b:lower()
  if la == lb then return a < b end
  return la < lb
end)
-- names is now { "Ada", "ada", "bob", "charlie" }

Using __lt instead of a comparator

If you sort the same shape repeatedly, attach a __lt metamethod to the element metatable and let the default < do the work:

local function record(fields)
  return setmetatable(fields, { __lt = function (a, b) return a.score < b.score end })
end

local players = {
  record { name = "Ada",  score = 88 },
  record { name = "Lin",  score = 95 },
  record { name = "Raj",  score = 73 },
}

table.sort(players)

The explicit comparator is clearer when you only sort once. The metamethod pays off when the same record type gets sorted in many places.

Sorting the keys of a dictionary

Tables have no intrinsic key order, so iterating a dictionary in sorted-key order means extracting the keys into an array, sorting that, then walking both:

local lines = { luaH_set = 10, luaH_get = 24, luaH_present = 48 }

local keys = {}
for k in pairs(lines) do keys[#keys + 1] = k end
table.sort(keys)

for i = 1, #keys do
  print(keys[i], lines[keys[i]])
end

The keys array is built up with pairs, which has no order guarantee. After table.sort runs on it, the keys are in lexicographic order, so the final for i = 1, #keys loop walks them deterministically. The original dictionary lines is never touched; only the auxiliary keys table gets reordered.

Output:

luaH_get	24
luaH_present	48
luaH_set	10

The boilerplate above is fine for one-off printing, but it gets old fast when sorted-key iteration is needed in several places. The standard trick is to push the key extraction and the sort into a stateful iterator, so call sites read like a regular pairs loop. This is the pairsByKeys pattern from Programming in Lua §19.3, and it generalizes to any comparator you want to pass in.

local function pairsByKeys(t, f)
  local a = {}
  for k in pairs(t) do a[#a + 1] = k end
  table.sort(a, f)
  local i = 0
  return function ()
    i = i + 1
    if a[i] == nil then return nil end
    return a[i], t[a[i]]
  end
end

for name, line in pairsByKeys(lines) do
  print(name, line)
end

This pairsByKeys pattern is from Programming in Lua §19.3 and is the canonical way to sort dictionary output in pure Lua.

Gotchas

Sparse tables and nil holes. If your sequence has gaps, #t is unreliable and table.sort is sorting an unpredictable prefix. Build a clean sequence first.

Mixed types in the array. table.sort({1, "2", 3}) raises “attempt to compare number with string”. Write a comparator that coerces with tostring or tonumber if you need to sort mixed data.

NaN as an element. Lua’s < returns false in both directions for NaN, so NaNs cluster at the start of a default sort and never resolve. Use math.huge for “missing” instead.

Modifying the table inside the comparator. Inserting, removing, or assigning keys while a sort is running is undefined. Don’t do it.

Forgetting that n is #t. A __len metamethod on the sorted table changes the sort’s upper bound. If you wrap an array in a class that lies about its length, table.sort will sort too few or too many elements.

table.sort(t) returns nothing. There is no non-mutating version. If you need a sorted copy, build it first: local out = table.move(t, 1, #t, 1, {}), then sort out.

See also