162

What's the most efficient way to determine if a table is empty (that is, currently contains neither array-style values nor dict-style values)?

Currently, I'm using next():

if not next(myTable) then
    -- Table is empty
end

Is there a more efficient way?

Note: The # operator does not suffice here, as it only operates on the array-style values in the table - thus #{test=2} is indistinguishable from #{} because both return 0. Also note that checking if the table variable is nil does not suffice as I am not looking for nil values, but rather tables with 0 entries (i.e. {}).

7 Answers 7

230

Your code is efficient but wrong. (Consider {[false]=0}.) The correct code is

if next(myTable) == nil then
   -- myTable is empty
end

For maximum efficiency you'll want to bind next to a local variable, e.g.,

...
local next = next 
...
... if next(...) ...

(When next is local, the code finds primitive function next by a constant-time indexing operation into an array of "upvalues." When next is left global, finding next involves indexing index the "environment" hash table, which contains the values of the global variables. This indexing operation is still constant-time, but it is significantly slower than the array lookup for a local variable.)

Sign up to request clarification or add additional context in comments.

9 Comments

Good point on the technical correctness; in the particular cases I've been utilizing the original code, false wouldn't be an expected key so the if not worked fine, but I'll probably make a habit of comparing to nil instead in the future, just as a good habit. And yes, I've been binding common utility functions to local vars for speed. Thanks for the input though.
Why do we gain speed by doing local next?
@Moberg This is due to how LUA handles its namespace. The very dumbed down version, is it will first climb up the local tables, so if there is a local next in the current block, it will use that, then climb up to the next block, and repeat. Once out of locals, it will only then use the Global Namespace. This is a dumbed down version of it, but in the end, it definitely means the difference in regards to program speed.
@Moberg at run time a global variable requires a hash-table lookup but a local variable requires only an array lookup.
@HerlySQR It's not faster, it's more correct. if not will miss one case where the table is not empty, as described in the answer, for the table {[false]=0}. next() returns the key, which is false in that example, and if not false will mislead you into thinking the table is empty. The == null approach will work correctly even in that case.
|
2

better to avoid the evaluation of __eq if overloaded.

if rawequal(next(myTable), nil) then
   -- myTable is empty
end

or

if type(next(myTable)) == "nil" then
   -- myTable is empty
end

2 Comments

I'm a Lua noob trying to understand why this answer was down voted. I'm guessing it is because in Lua, "if two objects have different metamethods, the equality operation results in false, without even calling any metamethod". (The quote is at the bottom of this page from Programming in Lua on lua.org ). Does that remove the need to avoid __eq overloading for nil?
SansWit: Yes, it does.
1

One possibility would be to count the number of elements, by using the metatable "newindex" key. When assigning something not nil, increment the counter (the counter could live in the metatable as well) and when assigning nil, decrement the counter.

Testing for empty table would be to test the counter with 0.

Here's a pointer to metatable documentation

I do like your solution though, and I honestly can't assume that my solution is faster overall.

3 Comments

The original question is not about counting just "array" entries.
0x6's suggestion isn't specific to array-style entries (newindex works for both numerical and non-numerical indices). However, the main issue would be detecting when nil is assigned, since __newindex does not trigger if the key already exists in the table.
For this trick to work, the metatable would have to implement both __index and __newindex, storing the actual data in a shadow table and keeping the real table empty so that __index will be invoked at all. Thinking out loud, I suspect that the raised cost of every single lookup can't be worth it.
0

This is probably what you wanted:

function table.empty (self)
    for _, _ in pairs(self) do
        return false
    end
    return true
end

a = { }
print(table.empty(a))
a["hi"] = 2
print(table.empty(a))
a["hi"] = nil
print(table.empty(a))

Output:

true
false
true

3 Comments

next() is more efficient (and more concise) than looping over pairs().
In fact, looping over pairs() is essentially just using the next() technique, but with more overhead.
Also, writing into the standard table library is not recommended.
-5

try serpent, work for me

serpent = require 'serpent'

function vtext(value)
  return serpent.block(value, {comment=false})
end

myTable = {}

if type(myTable) == 'table' and vtext(myTable) == '{}' then
   -- myTable is empty
end

Comments

-7

I know this is old, and I could be misunderstanding you somehow, but it you just want the table to be empty, that is, unless you are just checking if it is and you don't actually want or need it to be empty, you can clear it by simply recreating it, unless I'm mistaken. this can be done with the below syntax.

yourtablename = {} -- this seems to work for me when I need to clear a table.

1 Comment

That's not the question.
-7

How about this ?

if endmyTable[1] == nil then
  -- myTable is empty
end

2 Comments

This won't work on a table that as strings as index's
Or a table that doesn't have a value at index 1. Consider {[2] = true}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.