Lua - handling length of Lists



When we represent a sparse list using table as we discussed in Lua - Sparse Lists chapter, the standard method to get length of list using # operator may not work properly. To handle such a situation, we can use __len() metamethod. __len(table) is called when # operator is called to get the length of the table/list.

Syntax

__len(table)

where

  • table represents the current table whose behavior is to be modified.

Example - Problem in length Calculation

# operator tries to get length of a list assuming indexes in contigous order. In case of sparse lists, behavior is not correct as shown below:

main.lua

-- list with non-contigous indexes
local list = {[1]="apple",[3]="banana",[5]="mango"}

-- get length of the list
print(#list)

-- iterate List
for index, value in ipairs(list) do
   print(string.format("Index: %s, Value: %s", index, value))
end

-- get unassigned Value
print(list[2])

Output

When we run the above code, we will get the following output−

1
Index: 1, Value: apple
nil

As we can see, # is not able to calculate length of list correctly. Even ipairs() iterators stops when it encounters nil or unassigned index.

Example - Solution to correctly calculate length

We can implement __len(table) metamethod to calculate length correctly as shown below−

main.lua

-- list with non-contigous indexes
local list = {[1]="apple",[3]="banana",[5]="mango"}

-- create a metatable for length
local metatableLength = {
   __len = function(table)
      local items = 0
      -- iterate through all entries	  
      for key, _ in pairs(table) do
         items = items + 1
      end
      return items
   end
}

-- set the metatable
setmetatable(list, metatableLength)

-- get length of the list
print(#list)

-- iterate List
for key, value in pairs(list) do
   print(string.format("Key: %s, Value: %s", key, value))
end

Output

When the above code is built and executed, it produces the following result −

3
Key: 3, Value: banana
Key: 1, Value: apple
Key: 5, Value: mango

Explanation

  • local list = {[1]="apple",[3]="banana",[5]="mango"} is used to create a sparse list with non-contigous keys or indexes.

  • local metatableLength is used to create a meta table with __len() method.

  • __len = function(table) is the implementation of metamethod __len(table) method.

  • for key, _ in pairs(table) do is the iteration of table's entries and we're counting the entries and returning the same.

  • When print(#list) is called then length of list is called via __len() method implementation.

  • for key, value in pairs(list) do is used to perform navigation on table instead of ipairs() earlier to print key-value pairs of the list.

lua_metatables_with_lists.htm
Advertisements