Module:AutosortTable

--[[ AutosortTable: Creates a table which is automatically sorted Usage: (Remove the hidden comments before use)

]] local _module = {} _module.create = function(frame) local args = frame.args -- Named parameters local class = args.class local style = args.style local sep = args.separator local order = args.order local desc = args.descending or "" local nsort = args.numeric or "" local hidden = args.hidden or "" local header = args.header local footer = args.footer local colstyle = args.colstyle local rowheader = args.rowheader or "" local caption = args.caption -- Frequently-used functions local strIndexOf = mw.ustring.find local strSplit = mw.text.split local strSub = mw.ustring.sub local strTrim = mw.text.trim local seplen = #sep local nsortLookup, descLookup, hiddenLookup, rowHeading = {}, {}, {}, {} -- Create the table local html = mw.html.create local htable = html:tag('table') if class then htable:attr('class', class) end if style then htable:attr('style', style) end if caption then local hcaption = htable:tag('caption') hcaption:wikitext(caption) end

-- Parses a row string. The 'key' parameter is used to assign a unique key to the result so that equal rows do not cause sort errors. local parse = function(s, key) local css local firstSep = strIndexOf(s, sep, 1, true) if firstSep == 1 then -- no CSS css = nil s = strSub(s, seplen + 1, -1) else  -- CSS before first separator css = strSub(s, 1, firstSep - 1) s = strSub(s, firstSep + seplen, -1) end return {key = key, css = css, data = strSplit(s, sep, true)} end --       Writes a row to the table.        css: CSS to apply to the row        data: The data (cells) of the row        _type: Can be 'header', 'footer' or nil. local writeHtml = function(css, data, _type) local row = htable:tag('tr') if css then row:attr('style', strTrim(css)) end for i, v in ipairs(data) do           if not hiddenLookup[i] then local cell if _type == 'header' then -- Header: use the 'th' tag with scope="col" cell = row:tag('th') cell:attr('scope', 'col') elseif _type == 'footer' then -- Footer: Mark as 'sortbottom' so that it does not sort when the table is made user-sortable -- with the 'wikitable sortable' class cell = row:tag('td') cell:attr('class', 'sortbottom') else if rowHeading[i] then -- Cell is a row heading cell = row:tag('th') cell:attr('scope', 'row') else -- Ordinary cell cell = row:tag('td') end local cellCss = colstyle and colstyle[i] if cellCss then cell:attr('style', strTrim(cellCss)) end  -- Apply the column styling, if necessary end cell:wikitext(strTrim(v)) end end return row end -- Parse the column styles if colstyle then colstyle = parse(colstyle, -1).data end -- Write the header first if header then local headerData = parse(header) writeHtml(headerData.css, headerData.data, 'header') end -- Parse the data local data = {} for i, v in ipairs(frame.args) do data[i] = parse(v, i) end order = strSplit(order, '%s*,%s*') nsort = strSplit(nsort, '%s*,%s*') desc = strSplit(desc, '%s*,%s*') hidden = strSplit(hidden, '%s*,%s*') rowheader = strSplit(rowheader, '%s*,%s*')

for i, v in ipairs(order) do order[i] = tonumber(v) end for i, v in ipairs(nsort) do nsortLookup[tonumber(v) or -1] = true end for i, v in ipairs(desc) do descLookup[tonumber(v) or -1] = true end for i, v in ipairs(hidden) do hiddenLookup[tonumber(v) or -1] = true end for i, v in ipairs(rowheader) do rowHeading[tonumber(v) or -1] = true end

--Sorting comparator function. local sortFunc = function(a, b)       local ad, bd = a.data, b.data for i = 1, #order do           local index = order[i] local ai, bi = ad[index], bd[index] if nsortLookup[index] then -- Numeric sort. Find the first occurrence of a number and use it. Decimal points are allowed. Scientific notation not supported. ai = tonumber( (ai:find('.', 1, true) and ai:match('[+-]?%d*%.%d+') or ai:match('[+-]?%d+')) or 0 ) bi = tonumber( (bi:find('.', 1, true) and bi:match('[+-]?%d*%.%d+') or bi:match('[+-]?%d+')) or 0 ) end if ai ~= bi then if descLookup[index] then return ai > bi else return ai < bi end end end return a.key < b.key end table.sort(data, sortFunc) -- Write the sorted data to the HTML output for i, v in ipairs(data) do writeHtml(v.css, v.data, nil) end -- Write the footer if footer then local footerData = parse(footer) writeHtml(footerData.css, footerData.data, 'footer') end

return tostring(html) end

return _module