Skip to content

Commit 7650113

Browse files
authored
Merge pull request #2105 from Omikhleia/refactor-collated-sort
2 parents 4fbb6c0 + 3854be8 commit 7650113

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

core/utilities/sorting.lua

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
--- Table sorting with language-dependent collation.
22
-- @module SU.sorting
3+
--
34

45
local icu = require("justenoughicu")
56

67
local collatedSort = {
78
-- No ICU for language "und", fallback to 'natural' table.sort
8-
und = function (t, _)
9-
table.sort(t)
9+
und = function (t, _, comparator)
10+
if comparator then
11+
table.sort(t, function (e1, e2)
12+
return comparator(e1, e2, function (s1, s2)
13+
return s1 < s2 and -1 or s1 > s2 and 1 or 0
14+
end)
15+
end)
16+
else
17+
table.sort(t)
18+
end
1019
end,
1120
}
1221

1322
setmetatable(collatedSort, {
14-
__call = function (self, t, options)
23+
__call = function (self, t, options, comparator)
1524
local lang = SILE.settings:get("document.language")
1625
if self[lang] and type(self[lang]) == "function" then
1726
-- Allow overriding ICU for some languages, typically "und"
18-
return self[lang](t, options)
27+
return self[lang](t, options, comparator)
1928
end
2029

2130
if self[lang] and type(self[lang]) == "table" then
@@ -25,8 +34,17 @@ setmetatable(collatedSort, {
2534
-- Be efficient: create the collator once before sorting.
2635
-- I don't think we need to cache it, still.
2736
local collator = icu.collation_create(lang, options or {})
28-
table.sort(t, function (s1, s2)
37+
38+
local stringCompareClosure = function (s1, s2)
2939
return icu.compare(collator, s1, s2)
40+
end
41+
table.sort(t, function (e1, e2)
42+
-- Allow custom comparison function, notably for complex objects
43+
-- Pass the stringCompare function so that it can be used.
44+
if comparator then
45+
return comparator(e1, e2, stringCompareClosure)
46+
end
47+
return stringCompareClosure(e1, e2) < 0
3048
end)
3149
icu.collation_destroy(collator)
3250
end,

justenough/justenoughicu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ int je_icu_compare(lua_State *L) {
483483
return luaL_error(L, "Internal failure to perform comparison");
484484
}
485485

486-
lua_pushboolean(L, result == UCOL_LESS);
486+
lua_pushinteger(L, result); // UCOL_EQUAL(0), UCOL_GREATER(1), UCOL_LESS(-1)
487487
return 1;
488488
// IMPLEMENTATION NOTE FOR PORTABILITY
489489
// Good news, ucol_strcollUTF8 was introduced in ICU 50.

spec/utilities_spec.lua

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,44 @@ describe("SILE.utilities", function ()
307307
"Jean-Paul",
308308
}, sortme)
309309
end)
310+
it("should sort complex tables with callback comparison function", function ()
311+
local sortme = {
312+
{ name = "Jean", age = 30 },
313+
{ name = "Charlie", age = 25 },
314+
{ name = "Bob", age = 30 },
315+
{ name = "Alice", age = 25 },
316+
}
317+
SU.collatedSort(sortme, nil, function (a, b, stringCompare)
318+
-- Sort by ascending age then ascending name
319+
if a.age < b.age then return true end
320+
if a.age > b.age then return false end
321+
return stringCompare(a.name, b.name) < 0
322+
end)
323+
assert.is.same({
324+
{ name = "Alice", age = 25 },
325+
{ name = "Charlie", age = 25 },
326+
{ name = "Bob", age = 30 },
327+
{ name = "Jean", age = 30 },
328+
}, sortme)
329+
local namesAndYears = {
330+
{ name = "Alice", year = 2005 },
331+
{ name = "Charlie", year = 1995 },
332+
{ name = "Bob", year = 1990 },
333+
{ name = "Alice", year = 1995 }
334+
}
335+
SU.collatedSort(namesAndYears, nil, function (a, b, stringCompare)
336+
local nameCompare = stringCompare(a.name, b.name)
337+
if nameCompare < 0 then return true end
338+
if nameCompare > 0 then return false end
339+
return a.year < b.year
340+
end)
341+
assert.is.same({
342+
{ name = "Alice", year = 1995 },
343+
{ name = "Alice", year = 2005 },
344+
{ name = "Bob", year = 1990 },
345+
{ name = "Charlie", year = 1995 },
346+
}, namesAndYears)
347+
end)
310348
end)
311349
end)
312350
end)

0 commit comments

Comments
 (0)