Skip to content

Commit

Permalink
feat(core): Extend SU.collatedSort for complex table sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
Omikhleia authored and Didier Willis committed Sep 10, 2024
1 parent 8bed2e6 commit 3854be8
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
28 changes: 23 additions & 5 deletions core/utilities/sorting.lua
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
--- Table sorting with language-dependent collation.
-- @module SU.sorting
--

local icu = require("justenoughicu")

local collatedSort = {
-- No ICU for language "und", fallback to 'natural' table.sort
und = function (t, _)
table.sort(t)
und = function (t, _, comparator)
if comparator then
table.sort(t, function (e1, e2)
return comparator(e1, e2, function (s1, s2)
return s1 < s2 and -1 or s1 > s2 and 1 or 0
end)
end)
else
table.sort(t)
end
end,
}

setmetatable(collatedSort, {
__call = function (self, t, options)
__call = function (self, t, options, comparator)
local lang = SILE.settings:get("document.language")
if self[lang] and type(self[lang]) == "function" then
-- Allow overriding ICU for some languages, typically "und"
return self[lang](t, options)
return self[lang](t, options, comparator)
end

if self[lang] and type(self[lang]) == "table" then
Expand All @@ -25,8 +34,17 @@ setmetatable(collatedSort, {
-- Be efficient: create the collator once before sorting.
-- I don't think we need to cache it, still.
local collator = icu.collation_create(lang, options or {})
table.sort(t, function (s1, s2)

local stringCompareClosure = function (s1, s2)
return icu.compare(collator, s1, s2)
end
table.sort(t, function (e1, e2)
-- Allow custom comparison function, notably for complex objects
-- Pass the stringCompare function so that it can be used.
if comparator then
return comparator(e1, e2, stringCompareClosure)
end
return stringCompareClosure(e1, e2) < 0
end)
icu.collation_destroy(collator)
end,
Expand Down
2 changes: 1 addition & 1 deletion justenough/justenoughicu.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ int je_icu_compare(lua_State *L) {
return luaL_error(L, "Internal failure to perform comparison");
}

lua_pushboolean(L, result == UCOL_LESS);
lua_pushinteger(L, result); // UCOL_EQUAL(0), UCOL_GREATER(1), UCOL_LESS(-1)
return 1;
// IMPLEMENTATION NOTE FOR PORTABILITY
// Good news, ucol_strcollUTF8 was introduced in ICU 50.
Expand Down
38 changes: 38 additions & 0 deletions spec/utilities_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,44 @@ describe("SILE.utilities", function ()
"Jean-Paul",
}, sortme)
end)
it("should sort complex tables with callback comparison function", function ()
local sortme = {
{ name = "Jean", age = 30 },
{ name = "Charlie", age = 25 },
{ name = "Bob", age = 30 },
{ name = "Alice", age = 25 },
}
SU.collatedSort(sortme, nil, function (a, b, stringCompare)
-- Sort by ascending age then ascending name
if a.age < b.age then return true end
if a.age > b.age then return false end
return stringCompare(a.name, b.name) < 0
end)
assert.is.same({
{ name = "Alice", age = 25 },
{ name = "Charlie", age = 25 },
{ name = "Bob", age = 30 },
{ name = "Jean", age = 30 },
}, sortme)
local namesAndYears = {
{ name = "Alice", year = 2005 },
{ name = "Charlie", year = 1995 },
{ name = "Bob", year = 1990 },
{ name = "Alice", year = 1995 }
}
SU.collatedSort(namesAndYears, nil, function (a, b, stringCompare)
local nameCompare = stringCompare(a.name, b.name)
if nameCompare < 0 then return true end
if nameCompare > 0 then return false end
return a.year < b.year
end)
assert.is.same({
{ name = "Alice", year = 1995 },
{ name = "Alice", year = 2005 },
{ name = "Bob", year = 1990 },
{ name = "Charlie", year = 1995 },
}, namesAndYears)
end)
end)
end)
end)

0 comments on commit 3854be8

Please sign in to comment.