How to get number of entries in a Lua table?
Asked Answered
D

11

197

Sounds like a "let me google it for you" question, but somehow I can't find an answer. The Lua # operator only counts entries with integer keys, and so does table.getn:

tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl))   -- prints "1     1"

count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count)            -- prints "2"

How do I get the number of all entries without counting them?

Dugong answered 24/4, 2010 at 19:10 Comment(7)
@lhf: I have written a serializer which remembers every object it has seen, and the next time it sees it it emits an integer reference instead of the object. The natural way to write this is something like dictionary[value] = #dictionary + 1, where # represents the number of all objects. What I wonder is why you don't want this: in all sane use cases for # (see answer by kaizer.se), the count of all objects is exactly equal to what # already returns; it seems like making # count everything is strictly an improvement. Of course I'm a Lua newbie and might be missing the point.Dugong
@lhf Another use example: retrieve a lot of data into a table, where each data item has a unique string identifier. I use this identifier as the key because I'll be looking up by it later on. I now want to print the number of data items processed. I have to keep a counter and manually increment it for every row. Certainly not a big deal, but it is unusual not to be able to tell something like this without counting, and since you asked "why"... :)Dugong
The table is the best place to keep the information about the current object count, when used as container. For example when the table is used as a Set.Galeiform
@lhf: I've also got a use case where I need to know the number, in this case I need to know if there's only one item left in a table, in which case I handle it differently to if there are many items. If the answer is that we count them that's fine though; I'd guess a function that just had the answer would cost us performance elsewhere (such a feature would probably require lua to test the new and old value for nil every time we set a table value and then update a counter accordingly)Gladine
@Alternator, to test whether there is exactly one pair in a table, use next(t)~=nil and next(next(t))==nil.Canarese
Remark, it appears that the table doesn't really store the number of hash items (from the source code lua.org/source/5.3/ltable.c.html#luaH_newkey , lua.org/source/5.3/ltable.c.html#numusehash), so asymptotically speaking looping over the keys is the fastest way.Rebroadcast
Hahah... your "let me google it for you" type question is literally the first result for me in a search... though I'm using DuckDuckGo.Bolten
G
181

You already have the solution in the question -- the only way is to iterate the whole table with pairs(..).

function tablelength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

Also, notice that the "#" operator's definition is a bit more complicated than that. Let me illustrate that by taking this table:

t = {1,2,3}
t[5] = 1
t[9] = 1

According to the manual, any of 3, 5 and 9 are valid results for #t. The only sane way to use it is with arrays of one contiguous part without nil values.

Galeiform answered 24/4, 2010 at 19:14 Comment(8)
Well, he said "without counting them", but it's possible there is no other wayTowline
According to the manual, any of 3, 5 and 9 are valid results for #t. According to the manual, calling # on non-sequences is undefined. That means that any result (-1, 3, 3.14, 5, 9) is valid.Huddleston
Regarding valid results: u0b34a0f6ae is correct for Lua 5.1, while cubuspl42 is correct for Lua 5.2. In either case, the whole thing is completely insane.Tania
It's better to have a basic fp utility based on reduce: github.com/sarimarton/.config/blob/master/hammerspoon/util/…Spiritism
@Spiritism And what are we supposed to do with this comment now that you've made that repo private / deleted it?Zoster
@Zoster sorry - gist.github.com/sarimarton/fc02d27fa7c06d296d99f858b1143e5aSpiritism
@Spiritism Uh... wow, that's the first time that's ever actually worked to get the information back. I don't even know how to react to that. 😵‍💫 I guess, thanks!Zoster
@rboy true but the point is, in Lua assigning nil to a table key means deleting it. There's no way to distinguish between keys with nil values and keys with no value, regardless of method.Nosedive
V
25

You can set up a meta-table to track the number of entries, this may be faster than iteration if this information is a needed frequently.

Valval answered 24/4, 2010 at 19:36 Comment(6)
Is there a convenient way to handle erasing entries with this method?Galeiform
Sadly, it appears the __newindex function doesn't fire on nil assignments unless the index doesn't exist, so it seems you'd have to funnel entry removal through a special function.Valval
You should store data in a separate table (for example accessible as upvalue for __index and __newindex). Then both __index and __newindex would fire for each table access. You should check if the performance is acceptable though.Lisandra
@Alexander: Ah yes, and then the next stumbling point: if you proxy the table, then the normal iteration by pairs doesn't work. This will be possible to solve in Lua 5.2, I heard.Galeiform
There would be __pairs and __ipairs metamethods in 5.2... If you want to do it in 5.1, you'd have to replace pairs() function with your own. But that's probably too much. :-)Lisandra
Not helpful. An example would be nice.Pignut
I
16

The easiest way that I know of to get the number of entries in a table is with '#'. #tableName gets the number of entries as long as they are numbered:

tbl={
    [1]
    [2]
    [3]
    [4]
    [5]
}
print(#tbl)--prints the highest number in the table: 5

Sadly, if they are not numbered, it won't work.

Ileostomy answered 2/11, 2017 at 11:53 Comment(0)
H
9

There's one way, but it might be disappointing: use an additional variable (or one of the table's field) for storing the count, and increase it every time you make an insertion.

count = 0
tbl = {}

tbl["test"] = 47
count = count + 1

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

There's no other way, the # operator will only work on array-like tables with consecutive keys.

Hooknosed answered 25/4, 2010 at 14:13 Comment(4)
This can be automated with a proxy table and metamethods, as mentioned by ergosys's answerAureomycin
I got the impression from the comments that the proxytable/metamethods thing doesn't fully support this scenario yet, so I'll accept this as the best way currently available.Dugong
Counting is the only way for tables, and adding a lines when creating the tables is better than a function to count them every time you need the count. You can add a key at the end with the value set to the count.Lascar
Unfortunately you also need to check before inserting a value if the key is unique, ie does not already exists in the table; otherwise, element count should not be increased, as you're just replacing one value with another, not adding a new element.Rambouillet
Y
7
function GetTableLng(tbl)
  local getN = 0
  for n in pairs(tbl) do 
    getN = getN + 1 
  end
  return getN
end

You're right. There are no other way to get length of table

Yesseniayester answered 17/11, 2020 at 19:28 Comment(0)
K
4

You could use penlight library. This has a function size which gives the actual size of the table.

It has implemented many of the function that we may need while programming and missing in Lua.

Here is the sample for using it.

> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3 
> a['blah'] = 24

> #a
0

> tablex.size(a)
3
Keeley answered 1/7, 2019 at 12:21 Comment(1)
Yeah but all that does is for k in pairs(t) do i = i + 1 end so it's no better than other options on this topic already and adds extra overhead of using another libShakitashako
L
1
local function CountedTable(x)
    assert(type(x) == 'table', 'bad parameter #1: must be table')

    local new_t = {}
    local mt = {}

    -- `all` will represent the number of both
    local all = 0
    for k, v in pairs(x) do
        all = all + 1
    end

    mt.__newindex = function(t, k, v)
        if v == nil then
            if rawget(x, k) ~= nil then
                all = all - 1
            end
        else
            if rawget(x, k) == nil then
                all = all + 1
            end
        end

        rawset(x, k, v)
    end

    mt.__index = function(t, k)
        if k == 'totalCount' then return all
        else return rawget(x, k) end
    end

    return setmetatable(new_t, mt)
end

local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }

assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)
Lymph answered 9/5, 2016 at 11:24 Comment(2)
__newindex only call when a new key define, so there are no chance to call __newindex when we set nil to a exists key.Painting
Note the asserts that demonstrate CountedTable can indeed handle deletions. It does so by keeping the user-facing table completely empty, storing all the data in the shadow 'x' table that is closed over by the methods of the metatable 'mt'. Other posters remarked that the problem with this is that native pairs/ipairs won't iterate a CountedTable the way one would want them to.Ignatia
D
0

I stumbled upon this thread and want to post another option. I'm using Luad generated from a block controller, but it essentially works by checking values in the table, then incrementing which value is being checked by 1. Eventually, the table will run out, and the value at that index will be Nil.

So subtract 1 from the index that returned a nil, and that's the size of the table.

I have a global Variable for TableSize that is set to the result of this count.

function Check_Table_Size()
  local Count = 1
  local CurrentVal = (CueNames[tonumber(Count)])
  local repeating = true
  print(Count)
  while repeating == true do
    if CurrentVal ~= nil then
      Count = Count + 1
      CurrentVal = CueNames[tonumber(Count)]
     else
      repeating = false
      TableSize = Count - 1
    end
  end
  print(TableSize)
end
Dynel answered 20/9, 2020 at 21:57 Comment(0)
S
0

Found some solution that works fine for me.

local someTable = {3, 4, 5}
local table_size = #someTable
print("The table has [" .. table_size .. "] items")
Sexivalent answered 14/5, 2023 at 22:12 Comment(0)
P
0

The simplest way I found was to implement a metatable, such as:

setmetatable(tbl,{__index={len=function(len) local incr=0 for _ in pairs(len) do incr=incr+1 end return incr end}})

This allows you to call a simple len() function on your table, like so:

print(tbl:len())
Pignut answered 26/3 at 19:17 Comment(0)
L
-1

seems when the elements of the table is added by insert method, getn will return correctly. Otherwise, we have to count all elements

mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))

It will print 2 correctly

Love answered 25/10, 2017 at 6:4 Comment(2)
This is wrong... as mentioned in the answers above getn or # only counts the integer indices and they only works when the indices are consecutiveRebroadcast
table.insert also isn't special, except that you're letting it create numerically-indexed entries in the table. You could replace those two table.insert calls with mytable[1] = element1 and mytable[2] = element2 and the result of this code would be exactly the same. Which is why table.getn(mytable) returns 2.Zoster

© 2022 - 2024 — McMap. All rights reserved.