Tables

Table Flat

Access and write tables using dot "." notation, also known as "flat path". Similar to TweakDB Flat logic but for any kind of LUA table.

Definitions

tFlat.get(obj: table, path: string, opt default: any) -> value/default
tFlat.has(obj: table, path: string) -> boolean
tFlat.set(obj: table, path: string, value: any) -> table
tFlat.insert(obj: table, path: string, value: any) -> table
tFlat.delete(obj: table, path: string) -> table

Usage examples

All code examples below use this predefined table data. Note that items is an associative object and elements is a sequential table.

local myTable = {
    ready = true,
    config = {
        items = {
            item1 = true,
            item2 = false
        },
        elements = {
           'element1',
           'element2'
        }
    }
}

Get value

local item2 = tFlat.get(myTable, 'config.items.item2')
-- false

local element2 = tFlat.get(myTable, 'config.elements.2')
-- element2

Has value

local hasItem2 = tFlat.has(myTable, 'config.items.item2')
-- true

local hasElement2 = tFlat.has(myTable, 'config.elements.2')
-- true

Set value

tFlat.set(myTable, 'config.items.item2', true)

tFlat.set(myTable, 'config.elements.2', 'newElement2')
myTable = {
    ["ready"] = true,
    ["config"] = {
        ["items"] = {
            ["item1"] = true,
            ["item2"] = true,
        },
        ["elements"] = {
            "element1",
            "newElement2",
        },
    },
}

Insert sequential table value

tFlat.insert(myTable, 'config.elements', 'element3')
myTable = {
    ["ready"] = true,
    ["config"] = {
        ["items"] = {
            ["item1"] = true,
            ["item2"] = false,
        },
        ["elements"] = {
            "element1",
            "element2",
            "element3",
        },
    },
}

Delete value

tFlat.delete(myTable, 'config.items.item1')

tFlat.delete(myTable, 'config.elements.1')
myTable = {
    ["ready"] = true,
    ["config"] = {
        ["items"] = {
            ["item2"] = false,
        },
        ["elements"] = {
            "element2",
        },
    },
}

Source code

tFlat = {

    get = function(obj, path, default)

        -- get path array
        path = tFlat.split(path)

        -- vars
        local length = #path
        local current = obj
        local key

        -- loop through path
        for index = 1, length, 1 do

            -- current key
            key = path[ index ]

            -- convert key to number (sequential table)
            if tonumber(key) then
                key = tonumber(key)
            end

            -- stop searching if a child object is missing
            if current[ key ] == nil then
                return default
            end

            current = current[ key ]

        end

        return current

    end,

    has = function(obj, path)
        return tFlat.get(obj, path) ~= nil
    end,

    set = function(obj, path, val)

        -- get path array
        path = tFlat.split(path)

        -- vars
        local length = #path
        local current = obj
        local key

        -- loop through path
        for index = 1, length, 1 do

            -- current key
            key = path[ index ]

            -- convert key to number (sequential table)
            if tonumber(key) then
                key = tonumber(key)
            end

            -- set value on last key
            if index == length  then
                current[ key ] = val

            -- current key exists
            elseif current[ key ] then

                if type(current[ key ]) ~= 'table' then
                    current[ key ] = {}
                end

                current = current[ key ]

            -- current key doesn't exist
            else
                current[ key ] = {}
                current = current[ key ]

            end

        end

        -- return
        return obj

    end,

    insert = function(obj, path, val)

        -- get target
        local target = tFlat.get(obj, path)

        -- check if table and sequential
        if type(target) == 'table' and tFlat.isSequential(target) then
            table.insert(target, val)
        end

        -- return
        return obj

    end,

    delete = function(obj, path)

        -- get path array
        path = tFlat.split(path)
        
        -- vars
        local length = #path
        local current = obj
        local key

        -- loop through path
        for index = 1, length, 1 do

            -- current key
            key = path[ index ]

            -- convert key to number (sequential table)
            if tonumber(key) then
                key = tonumber(key)
            end

            -- set value on last key
            if index == length then
                current[ key ] = nil
            else
                current = current[ key ] or {}
            end

        end

        -- return
        return obj

    end,

    split = function(s)

        s = tostring(s)
        local fields = {}
        local pattern = string.format("([^%s]+)", '.')

        string.gsub(s, pattern, function(c)
            fields[ #fields + 1 ] = c
        end)

        return fields

    end,

    isSequential = function(array)
        for k, _ in pairs(array) do
            if type(k) ~= "number" then
                return false
            end
        end
        return true
    end,

}

Last updated