Figuring out whether a table is empty in Lua

I’m currently working on a tagging system for this website using pandoc, and I’ve decided to take a little break to write about a function I wrote for the system. My tags aren’t ready yet (I don’t have a deduper yet, for example, nor do I have any setup to actually make the tag pages), by the way. I’m just proud of myself for getting this function together:

local function is_empty(tbl)
    if type(tbl) ~= "table" then return false end
    if next(tbl) == nil then return true end
    for k, v in pairs(tbl) do
        if type(v) ~= "table" then return false end
        if type(k) ~= "number" then return false end
        if k == #tbl then
            return is_empty(v)
        else
            if not is_empty(v) then return false end
        end
    end
end

I’m not sure if is_empty is really a good name for it, because it doesn’t exactly tell you that (or not only that), but whether a table contains only empty tables. I decided to implement it because of the way pandoc implements metadata, and I use that metadata (namely the tags field) to build my tag file that will, hopefully, build my tag index. I could’ve just checked for the emptiness of, say, tags[1][1].c, but I think that’s fragile (what if pandoc changes the structure?) and ugly. Thus, this function. Let’s talk about it.

if type(tbl) ~= "table" then return false end

Obviously, if the thing passed to is_empty isn’t a table, it’s not an empty table. Easy so far.

if next(tbl) == nil then return true end

I needed to check the length of tbl next: if it’s {}, we can go ahead and mark it as empty too. It wasn’t as easy as it looked at first though: I was using #tbl == 0, which only checks for the numerically-indexed keys in tbl. Pandoc eventually has keys like "c" and "t", which aren’t numbers, so my function was returning true on tables that weren’t empty. next() gets the next index of the table (I think; I saw the answer on Stack Overflow and the documentation is sparse), and works with both numerical and other - indexed keys.

for k, v in pairs(tbl) do

Here we go. Into the depths of the table. pairs() recurses through the table, returning keys and values (bound here to k and v).

    if type(v) ~= "table" then return false end

Another easy one: if v is not a table, it’s something important like 1, "hello", or whatever. So the table isn’t empty.

    if type(k) ~= "number" then return false end

Same idea as the v typecheck above: if k isn’t a number (the default key-type in Lua), then it’s holding information of some kind. For example, if you have a table that’s something like { junk = {} } I’m assuming you want junk to be an empty table, or you’d set it to nil and delete it. That being said, I might revisit this later if necessary.

    if k == #tbl then
        return is_empty(v)
    else
        if not is_empty(v) then return false end
    end

I’ve put all the recursion stuff together at the end. I’m not sure if it’s tail recursion, but I don’t really care for this use-case. It works, and that’s good enough for me.

First, we check if we’re looking at the last item in the table. If so, we just check whether it is empty, using the same function we’re building. Otherwise, we look inside the table and if it’s not empty, we return false, or else we keep going with the for loop.

Now just to end everything:

end

Wrap it all up in the function is_empty, and you’ve got a good way to look inside a table to see if it’s turtles all the way down.