mediawikimediawiki-extensions

Best way to explode two strings and create combinations of values from each string


I have two parameters in a template, 'location' and 'locality_subjects'. Each can contain multiple values separated by a semicolon.

For instance:

|location=Bucks County, Pennsylvania;Chester County, Pennsylvania
|locality_subjects=Court records;Probate records;Vital records

I would like to create categories for each of these combinations:

[[Category:Bucks County, Pennsylvania, court records]]
[[Category:Bucks County, Pennsylvania, probate records]]
[[Category:Bucks County, Pennsylvania, vital records]]
[[Category:Chester County, Pennsylvania, court records]]
[[Category:Chester County, Pennsylvania, probate records]]
[[Category:Chester County, Pennsylvania, vital records]]

I can do it pretty clumsily by using something like this, assuming that the maximum number of values each parameter could potentially contain is 4:

[[Category:{{#explode:{{{location|}}}|;|0}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|0}}]]
[[Category:{{#explode:{{{location|}}}|;|0}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|1}}]]
[[Category:{{#explode:{{{location|}}}|;|0}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|2}}]]
[[Category:{{#explode:{{{location|}}}|;|0}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|3}}]]
[[Category:{{#explode:{{{location|}}}|;|1}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|0}}]]
[[Category:{{#explode:{{{location|}}}|;|1}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|1}}]]
[[Category:{{#explode:{{{location|}}}|;|1}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|2}}]]
[[Category:{{#explode:{{{location|}}}|;|1}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|3}}]]
[[Category:{{#explode:{{{location|}}}|;|2}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|0}}]]
[[Category:{{#explode:{{{location|}}}|;|2}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|1}}]]
[[Category:{{#explode:{{{location|}}}|;|2}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|2}}]]
[[Category:{{#explode:{{{location|}}}|;|2}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|3}}]]
[[Category:{{#explode:{{{location|}}}|;|3}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|0}}]]
[[Category:{{#explode:{{{location|}}}|;|3}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|1}}]]
[[Category:{{#explode:{{{location|}}}|;|3}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|2}}]]
[[Category:{{#explode:{{{location|}}}|;|3}}, {{#explode:{{lc:{{{locality_subjects|}}}|;|3}}]]

It seems like there must be a better way?


Solution

  • If you are willing to use Scribunto, you could create a module with a function that would produce a Cartesian product of all lists passed and format all items of it:

    local remove, insert, concat = table.remove, table.insert, table.concat
    local split, trim = mw.text.split, mw.text.trim
    local clone = mw.clone
    
    local function cartesian (sets)
        local product = {}
        local set = remove (sets)
        if #sets == 0 then
            for _, item in ipairs (set) do
                insert (product, { item })
            end
            return product
        end
        for __, first in ipairs (cartesian (sets)) do
            for _, second in ipairs (set) do
                insert (product, clone (first))
                insert (product [#product], second)
            end
        end
        return product
    end
    
    local function format_array (array, format, separator)
        local formatted = {}
        for _, combination in ipairs (array) do
            insert (formatted, format:format (unpack (combination)))
        end
        return concat (formatted, separator)
    end
            
    return {
        combine = function (frame)
            local sets = {}
            local delimiter = frame.args.delimiter or ';'
            local splitter = '%s*' .. delimiter .. '%s*'
            local default_format = {}
            for _, list in ipairs (frame.args) do
                sets[#sets + 1] = split (trim (list), splitter)
                insert (default_format, '%s')
            end
            local format = frame.args.format or concat (default_format, ', ')
            local separator = frame.args.separator or '\n'
            return format_array (cartesian (sets), format, separator)
        end
    }
    

    (see example, except there are colons before Category).

    To use it in in your template, add

    {{#invoke:Test/combine|combine
        | {{{location}}}
        | {{{locality_subjects}}}
        | format = [[Category:%s, %s]]
    }}