Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend SU.collatedSort for complex table sorting #2105

Merged
merged 1 commit into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Loading