Modul:Wikidata/Sorters
Vzhled
require "Modul:No globals"
local p = {}
local lib = require 'Modul:Wikidata/lib'
local Formatters = require 'Modul:Wikidata/Formatters'
local current_sorter, inverted, sorters
local function checkInvert(value)
if inverted then
return not value
end
return value and true
end
local function sortSame(first, second)
current_sorter = current_sorter + 1
if sorters[current_sorter] then
return sorters[current_sorter](first, second)
end
current_sorter = 1
return checkInvert(false)
end
local function sortBySnaktype(first, second)
if not (first and second) then
return checkInvert(second)
end
if first.mainsnak.snaktype == second.mainsnak.snaktype then
return sortSame(first, second)
end
if first.mainsnak.snaktype == 'value' and second.mainsnak.snaktype ~= 'value' then
return checkInvert(true)
elseif first.mainsnak.snaktype == 'somevalue' and second.mainsnak.snaktype == 'novalue' then
return checkInvert(true)
end
return checkInvert(false)
end
local function alphaSort(first, second)
if not (first and second) then
return checkInvert(second)
end
if not (lib.IsSnakValue(first.mainsnak) and lib.IsSnakValue(second.mainsnak)) then
return sortBySnaktype(first, second)
end
local first_value = Formatters.getRawValue(first.mainsnak)
local second_value = Formatters.getRawValue(second.mainsnak)
if first_value == second_value then
return sortSame(first, second)
end
local first_label = mw.wikibase.label(first_value)
local second_label = mw.wikibase.label(second_value)
if not (first_label and second_label) then
return checkInvert(first_label)
end
-- tohle přesunout do vlastního modulu
-- na Wikislovníku mají Modul:Collation
-- http://prirucka.ujc.cas.cz/?id=900
local first_chars, second_chars = mw.text.split(first_label, ""), mw.text.split(second_label, "")
if mw.ustring.lower(first_label) == mw.ustring.lower(second_label) then
if first_label == second_label then
return sortSame(first, second)
end
local i = 1
while true do
local first_char = first_chars[i]
local second_char = second_chars[i]
if first_char ~= second_char then
return checkInvert(first_char == mw.ustring.upper(first_char))
end
i = i + 1
end
end
local min_length = math.min(#first_chars, #second_chars)
local chars = "aábcčdďeéěfghiíjklmnňoópqrřsštťuúůvwxyýzž0123456789.,;?!:„“–|/\()[]‹›{}"
local function charValue(char)
return mw.ustring.match(chars, '()' .. mw.ustring.lower(char)) or (mw.ustring.len(chars) + 1)
end
for i = 1, min_length do
local first_charvalue = charValue(first_chars[i])
local second_charvalue = charValue(second_chars[i])
if first_charvalue ~= second_charvalue then
return checkInvert(first_charvalue < second_charvalue)
end
end
return checkInvert(#first_chars == min_length)
end
local function sortHasLabel(first, second)
if not (first and second) then
return checkInvert(second)
end
if not (lib.IsSnakValue(first.mainsnak) and lib.IsSnakValue(second.mainsnak)) then
return sortBySnaktype(first, second)
end
local first_value = Formatters.getRawValue(first.mainsnak)
local second_value = Formatters.getRawValue(second.mainsnak)
if first_value == second_value then
return sortSame(first, second)
end
local first_label = mw.wikibase.label(first_value)
local second_label = mw.wikibase.label(second_value)
if first_label and second_label then
return sortSame(first, second)
end
return checkInvert(first_label)
end
local function sortByDate(first, second)
local FirstValues, SecondValues
if first and first.qualifiers then
FirstValues = {}
for key, array in pairs(lib.props) do
for _, prop in ipairs(array) do
if first.qualifiers[prop] then
for _, snak in ipairs(first.qualifiers[prop]) do
if lib.IsSnakValue(snak) then
FirstValues[key] = Formatters.getRawValue(snak)
break
end
end
end
end
end
end
if second and second.qualifiers then
SecondValues = {}
for key, array in pairs(lib.props) do
for _, prop in ipairs(array) do
if second.qualifiers[prop] then
for _, snak in ipairs(second.qualifiers[prop]) do
if lib.IsSnakValue(snak) then
SecondValues[key] = Formatters.getRawValue(snak)
break
end
end
end
end
end
end
if not (FirstValues and SecondValues) then
return checkInvert(FirstValues)
end
if FirstValues.point or SecondValues.point then
if FirstValues.point and SecondValues.point then
if FirstValues.point == SecondValues.point then
return sortSame(first, second)
else
return checkInvert(FirstValues.point < SecondValues.point)
end
else
return checkInvert(FirstValues.point)
end
end
if not (SecondValues.begin or SecondValues.ending) then
if not (FirstValues.begin or FirstValues.ending) then
return sortSame(first, second)
else
return checkInvert(true)
end
end
if not (FirstValues.begin or FirstValues.ending) then
return checkInvert(false)
end
if FirstValues.begin and SecondValues.begin then
if FirstValues.begin ~= SecondValues.begin then
return checkInvert(FirstValues.begin < SecondValues.begin)
end
if FirstValues.ending and SecondValues.ending then
if FirstValues.ending ~= SecondValues.ending then
return checkInvert(FirstValues.ending < SecondValues.ending)
else
return sortSame(first, second)
end
end
if FirstValues.ending or SecondValues.ending then
return checkInvert(FirstValues.ending)
else
return sortSame(first, second)
end
end
if FirstValues.ending and SecondValues.ending then
if FirstValues.ending == SecondValues.ending then
if FirstValues.begin or SecondValues.begin then
return checkInvert(not FirstValues.begin)
else
return sortSame(first, second)
end
end
end
if FirstValues.begin and SecondValues.ending then
return checkInvert(FirstValues.begin < SecondValues.ending)
end
if FirstValues.ending and SecondValues.begin then
return checkInvert(not (SecondValues.begin < FirstValues.ending))
end
if FirstValues.ending and SecondValues.ending then
return checkInvert(FirstValues.ending < SecondValues.ending)
end
return checkInvert(false)
end
local function sortByRank(first, second)
if not (first and second) then
return checkInvert(second)
end
if first.rank == second.rank then
return sortSame(first, second)
end
if first.rank == 'preferred' and second.rank ~= 'preferred' then
return checkInvert(true)
elseif first.rank == 'normal' and second.rank == 'deprecated' then
return checkInvert(true)
end
return checkInvert(false)
end
local function sortByQuantity(first, second)
if not (first and second) then
return checkInvert(second)
end
if not (lib.IsSnakValue(first.mainsnak) and lib.IsSnakValue(second.mainsnak)) then
return sortBySnaktype(first, second)
end
local first_quantity = Formatters.getRawValue(first.mainsnak)
local second_quantity = Formatters.getRawValue(second.mainsnak)
if first_quantity == second_quantity then
return sortSame(first, second)
end
return checkInvert(first_quantity > second_quantity)
end
local Methods = {
alpha = {
datatypes = { 'wikibase-item', 'wikibase-property' },
sorter = alphaSort,
},
date = {
sorter = sortByDate
},
hasLabel = {
datatypes = { 'wikibase-item', 'wikibase-property' },
sorter = sortHasLabel
},
number = {
datatypes = { 'quantity' },
sorter = sortByQuantity,
},
rank = {
sorter = sortByRank
},
snaktype = {
sorter = sortBySnaktype
},
}
local function getSorter(method, curr_datatype)
if Methods[method] then
if Methods[method].datatypes then
for _, datatype in ipairs(Methods[method].datatypes) do
if curr_datatype == datatype then
return Methods[method].sorter
end
end
return error(lib.formatError('invalid-datatype', method))
end
return Methods[method].sorter
end
return error(lib.formatError('invalid-sort', method))
end
function p.sortStatements(statements, options)
current_sorter = 1
inverted = lib.IsOptionTrue(options, 'invert')
sorters = {}
local datatype = statements[math.random(#statements)].mainsnak.datatype
local methods = lib.textToTable(options.sort)
for key, method in ipairs(methods) do
table.insert(sorters, getSorter(method, datatype))
end
table.sort(statements, sorters[1])
-- @deprecated
return statements
end
return p