Jump to content

Module:Custom functions

From WhiskerWiki

Documentation for this module may be created at Module:Custom functions/doc

local cargo = mw.ext.cargo -- for cargo queries if needed
local html = mw.html.create




local cf = {}





























--[[ This function takes a cargo results array and a flat, 1D table
 containing strings representing field names and produces a nested bulleted wiki text list for display 
--]]
function cf.bulleted_list_multi(dict, field_names, show_field_names)
    -- Set default value for show_field_names if not provided
    if show_field_names == nil then
        show_field_names = true
    end

    local hierarchy = {}

    -- Build the hierarchy based on the dict and field_names
    for _, entry in ipairs(dict) do
        local current_node = hierarchy

        for _, field_name in ipairs(field_names) do
            -- Skip empty fields to omit them from the list
            if entry and entry[field_name] and entry[field_name] ~= "" then
                local value = entry[field_name]:gsub("%**$", "") -- Remove trailing asterisks
                current_node[value] = current_node[value] or {}
                current_node = current_node[value]
            else
                -- If the field is empty, skip this iteration
                break
            end
        end
    end

    -- Recursive function to render the hierarchy as a nested bulleted list using mw.html
    local function recursive_list_render(node, depth)
        local sorted_keys = {}
        for key, _ in pairs(node) do
            table.insert(sorted_keys, key)
        end
        table.sort(sorted_keys)

        local list = mw.html.create('ul')
        for _, key in ipairs(sorted_keys) do
            local list_item = mw.html.create('li')

            -- Determine if field names should be shown
            local display_text
            if show_field_names then
                local field_name_display = field_names[depth]:gsub("_", " ") -- Replace underscores with spaces
                display_text = field_name_display .. ": " .. key
            else
                display_text = key
            end

            -- Use mw.html:wikitext to add the content as wikitext
            if depth == #field_names and (string.lower(field_names[depth]) == "species" or string.lower(field_names[depth]) == "genus") then
                list_item:wikitext("''" .. display_text .. "''")
            else
                list_item:wikitext(display_text)
            end

            local sub_list = recursive_list_render(node[key], depth + 1)
            if sub_list then
                list_item:node(sub_list)
            end

            list:node(list_item)
        end
        return (#sorted_keys > 0) and list or nil
    end

    return tostring(recursive_list_render(hierarchy, 1))
end

















--[[
    This function takes a comma-delimited string and converts it into a table
    of trimmed values. Each value in the input string is separated by a comma,
    and leading and trailing whitespace from each value is removed.

    Parameters:
    csv_string (string|nil): A comma-delimited string containing values to be parsed.
                             If nil, an empty table is returned.

    Returns:
    table: A table containing the trimmed values from the input string.
           If the input is nil, an empty table is returned.

    Example usage:
    local result = cf.parse_csv_to_table(" value1 , value2 ,value3 ")
    -- result: {"value1", "value2", "value3"}

    local empty_result = cf.parse_csv_to_table(nil)
    -- empty_result: {}
--]]
function parse_csv_to_table(csv_string)
    -- Return an empty table if the input is nil
    if csv_string == nil then
        return {}
    end

    -- Check if the argument is a string
    if type(csv_string) ~= "string" then
        error("Invalid argument: csv_string must be a string")
    end

    local t = {}
    for field in csv_string:gmatch("[^,]+") do
        table.insert(t, mw.text.trim(field))
    end
    return t
end
cf.parse_csv_to_table = parse_csv_to_table










-- takes an array and surrounds its elements with specified characters
function cf.element_sandwicher(array, start_char, end_char)
  local result = {}
  local length = #array

  for i = 1, length do
    result[i] = start_char .. array[i] .. end_char
  end

  return result
end






--[[ this function finds thing that are in the first array that are not in the second.
I first used it making custom pages that display orhpaned Data namespace pages that can't find
their forward facing partner
--]]
function cf.in_a_not_b(a, b)
    local result = {}
    local set_b = {}

    -- Create a set of unique elements in table b for efficient lookup
    for _, value in ipairs(b) do
        set_b[value] = true
    end

    -- Create a set of unique elements in table a without duplicates
    local set_a = {}
    for _, value in ipairs(a) do
        if not set_a[value] then
            set_a[value] = true
            -- Check if the element in table a is not present in table b and collect non-matching elements
            if not set_b[value] then
                table.insert(result, value)
            end
        end
    end

    return result
end




-- This parses text that is a single text string separated by commas into an array
function cf.parse_arguments(text, format, link_text)
    if text then
        local text_table = {}  -- Table to store parsed text elements
        -- Iterate over the delimited string and extract each text element
        for item in text:gmatch("[^,]+") do
            if format == "page" then
                item = '[[' .. item .. ']]'  -- Surround item with double brackets
            elseif format == "ext_link" then
                item = "[" .. item .. " " .. link_text .. "]"
            elseif format == "italic page" then
                item = "<i>[[" .. item .. "]]</i>"    
        	else
            	item = item:gsub(",", ", ")  -- Add a space after each comma
        	end
        table.insert(text_table, item)
        end
        -- Iterate over the arguments table and concatenate them into a string
        local full_text = table.concat(text_table, ", ")
        return full_text
    else
        return full_text
    end
end









-- Helper function to get keys from a table
function keys(t)
    local keyset = {}
    local n = 0
    for k, _ in pairs(t) do
        n = n + 1
        keyset[n] = k
    end
    return keyset
end









 

--[[ function that parses a cargo_query result and converts it from a key/value
dictionary to a simple array where each element in the array has
a the raw text corresponding to the final table to display the 'fields' parameter
is necessary since the order of fields in Lua tables is not guaranteed
since they are implemented as hash tables. --]]
function cf.format_cargo_results(results, field_formats)
  
  -- Preserve the original order of the keys
  local originalKeyOrder = {}
  for key in string.gmatch(field_formats, "%s*([^,]+)") do
    table.insert(originalKeyOrder, key)
  end

  local table_data = {}
  
  -- Insert the keys as the first row in table_data, following the original order
  local columnHeaders = {}
  for _, originalKey in ipairs(originalKeyOrder) do
    table.insert(columnHeaders, originalKey)
  end
  table.insert(table_data, columnHeaders)

  -- Insert the rows
  for _, row in ipairs(results) do
    local rowData = {}
    for _, key in ipairs(originalKeyOrder) do
      table.insert(rowData, row[key])
    end
    table.insert(table_data, rowData)
  end

  return table_data
end





-- Loop through the rows in an input array and remove contiguous duplicates in each field
function cf.dupes_remover(input_array)
    local prev_values = {}
    for i, row in ipairs(input_array) do
        for j, field in ipairs(row) do
            -- Check if the current field value is the same as the previous one for this column
            if i > 1 and field == prev_values[j] then
                -- If so, set the field value to an empty string
                row[j] = ""
            else
                -- If not, update the previous value for this column
                prev_values[j] = field
            end
        end
    end
    return input_array
end





--[[ Loop through the cells in an array before displaying on a forward-facing
page and surrounding brackets to make them wiki page links. Default behvior
will convert all columns or a list of only columns desired can be specified.
header = false will not skip the first row.]]
function cf.bracketer(input_array, columns)
    local result = {}
    local column_indices = {}

    for i, row in ipairs(input_array) do
        local modified_row = {}

        for j, value in ipairs(row) do
            local modified_value = value
            -- Add brackets to the specified column values if columns are provided and the value is not blank or nil
            if column_indices[j] and value ~= "" and value ~= nil then
                modified_value = "[[" .. value .. "]]"
            end
            table.insert(modified_row, modified_value)
        end

        table.insert(result, modified_row)

        -- Process the first row to determine column indices
        if i == 1 then
            -- Identify the column indices to modify based on the provided column names or bracket all columns by default
            if columns then
                for k, column in ipairs(row) do
                    if string.find(columns, column) then
                        column_indices[k] = true
                    end
                end
            else
                for k = 1, #row do
                    column_indices[k] = true
                end
            end
        end
    end

    return result
end




-- functiont that extracts the official versions of the site names on the wiki
function cf.get_site_name()
    -- query cargo to get official compendium names
    local tables = "Compendium_info"
    local fields = "Site_number, Site_name, Site_name_long"
    local cargo_results = cargo.query(tables, fields)
    
    -- initialize the variables
    local site = {}
    
    -- for loop to extract the results from the cargo.query output
    for _, row in ipairs(cargo_results) do
        local site_num = tonumber(row.Site_number)
        site[site_num] = {
            name = row.Site_name,
            name_long = row.Site_name_long
        }
    end
    
    return site
end




-- simple function to capitalize only the first character of a string
function cf.first_char_caps(str)
    return str:gsub("^%l", string.upper)
end



-- simple function to return the field names of a returned Cargo query
function cf.get_field_names(cargo_query)
	for i, row in ipairs(cargo_query) do
	    local field_names = ""
	    for field_name, _ in pairs(row) do
	        field_names = field_names .. field_name .. ", "
	    end
	    field_names = field_names:sub(1, -3) -- Remove the trailing comma and space
	    return
	end
end


--[[function that take a dictionary and one focal key/field and displays a sorted
-- bulleted list of the unique values for that key/field.]]
function cf.bulleted_list(dict, field_name)
    local uniques_of_rank = {}

    for i = 1, #dict do
        local entry = dict[i]
        if entry and entry[field_name] then
            local focal_rank = entry[field_name]
            uniques_of_rank[focal_rank] = true
        else
            mw.log("Invalid entry at index " .. i .. " in dict")
        end
    end

    local sorted_rank = {}
    for focal_rank, _ in pairs(uniques_of_rank) do
        table.insert(sorted_rank, focal_rank)
    end
    table.sort(sorted_rank)

    local list = {}
    for i = 1, #sorted_rank do
        local item = sorted_rank[i]:gsub("%**$", "") -- Remove trailing asterisks
        local listItem = mw.html.create('li')
        if string.lower(field_name) == "species" or string.lower(field_name) == "genus" then
            listItem:wikitext("''[[" .. item .. "]]''")
        else
            listItem:wikitext("[[" .. item .. "]]")
        end
        table.insert(list, listItem)
    end

    if #list > 0 then
        local resultList = mw.html.create('ul')
        for i = 1, #list do
            resultList:node(list[i])
        end
        return tostring(resultList)
    else
        return nil
    end
end





























return cf
Cookies help us deliver our services. By using our services, you agree to our use of cookies.