53

I need to know how to retrieve the key set of a table in lua. for example, if I have the following table:

tab = {}
tab[1]='a'
tab[2]='b'
tab[5]='e'

I want to be retrieve a table that looks like the following:

keyset = {1,2,5}
hjpotter92
  • 78,589
  • 36
  • 144
  • 183
ewok
  • 20,148
  • 51
  • 149
  • 254

2 Answers2

59
local keyset={}
local n=0

for k,v in pairs(tab) do
  n=n+1
  keyset[n]=k
end

Note that you cannot guarantee any order in keyset. If you want the keys in sorted order, then sort keyset with table.sort(keyset).

lhf
  • 70,581
  • 9
  • 108
  • 149
  • 6
    there's no reason to even use the local n in there, you can just do a single line `table.insert(keyset, k)` inside the loop – Mike Corcoran Oct 01 '12 at 13:56
  • 12
    `table.insert` needs to calculate the length of the table on every iteration, so it is slower. Using a very simple benchmark to measure 10000 iterations of `n=n+1; keyset[n]=k` vs. `table.insert(keyset, k)` results in following numbers: `n`: 0.76s, `table.insert`: 1.34s – Michal Kottman Oct 01 '12 at 17:15
  • @MichalKottman While I don't see need to use table.insert, 0.6s difference for 10000 iterations is not that bad - how often do you make a keyset for a table with >10000 key-value mappings in lua? – Nulano Feb 06 '15 at 15:10
  • 13
    @MichalKottman `table.insert` does not need to calculate the length of the table (in any meaningful way). Tables that act strictly as lists maintain a cached length internally. However `table.insert` is still slower (as you mentioned), but only because of constant factors like function call overhead. In particular, `table.insert` should not be asymptotically slower. – tehtmi Aug 27 '15 at 09:38
  • 8
    You can also use `keyset[#keyset+1] = k` . – Chung-Yen Hung Jul 19 '17 at 09:00
  • 1
    Some LuaJIT timings (with the version embedded in LÖVE 0.10.2, on a late-2017 iMac): `table.insert` takes 0.0865 seconds, `n=n+1;table[n]` takes 0.0130 seconds, and `table[#table+1]` takes 0.083 seconds, all for 1000000 inserts. Seems that for the best raw performance you still want to explicitly increment your index. It also seems that `table.insert` and `#table+1` do not scale linearly, whereas the `n=n+1` method does. – fluffy Feb 04 '18 at 06:46
  • 3
    Apologies, I just re-checked my timings and they do all scale linearly after all. – fluffy Feb 04 '18 at 06:59
  • 1
    `table.insert(t,v)` does not need to compute the length of the table `t` on each iteration: the table internally has a field storing the "#table" value, and `table.insert` just uses it. But if you want to go a bit faster, set a temp variable `local insert=table.insert` before the loop, this avoid looking up multiple times the `"insert"` key in the `table` object; then call `insert(t,v)` instead of `table.insert(t,v)`. Lua allows the `table` object to be modified (even inside the loop) so each method call from it requires that lookup. – verdy_p Sep 26 '22 at 19:18
  • @verdy_p, that's not what the source says: https://www.lua.org/source/5.4/ltablib.c.html#tinsert – lhf Oct 01 '22 at 00:45
  • @lhf, that source line in ltablib.c is `lua_Integer e = aux_getn(L, 1, TAB_RW);` which gets the internal field of the table without computing the length by scanning it for the lowest index that is not string a non-null value. when you put something at an index that is higher than or equal to the value of this field, the filed is updated without scanning. That field is never decremented, it assumes speed for handling tables that form sequences, not for sparse tables filled randomly. – verdy_p Nov 25 '22 at 13:06
  • Note that such case in aux_getn is very tricky: it handles the possibiilty that the table may have a length method (and use it), otherwise it tries to find a "boundary" by using a binary search in some cases (this depends on whever the table has a hashed part or not; but for filling a table in sequence it uses an internal "alimit" field without performing any search., to avoid such scans as possible (that field may be reset if you perform operations on the table out of the sequence order) – verdy_p Nov 25 '22 at 13:19
4
local function get_keys(t)
  local keys={}
  for key,_ in pairs(t) do
    table.insert(keys, key)
  end
  return keys
end
Paul Hilliar
  • 579
  • 6
  • 8