• Subject: Re: Newbie question on pairs and ipairs
• From: Rici Lake <lua@...>
• Date: Thu, 16 Dec 2004 09:36:50 -0500

```
On 16-Dec-04, at 12:59 AM, Glenn Edgar wrote:

```
```Hi

```
The source code indicates that a lua table consists of two parts, an array part and a hash table part. It also appears that ipairs iterates over the
```array part and pairs iterates over both the array and hash table part.

```
```
```
Not quite. ipairs iterates over integer keys, starting from 1, until it finds one which is not in the table. It is quite possible for an integer key to be in the hash part of the table; you should regard the internal implementation of the table as two parts to be an implementation detail.
```
```
If my understanding is correct, I would like to ask the following question.
```
```
What is the best way to iterate over the keys that are only in that hash table
```part of the table?
```
```
```
The semantic definition of Lua avoids making reference to implementation details (which is correct, imho), so there is no way to do that, either with Lua or even with the C API.
```
```
The solution proposed by Asko is one possibility, but it suffers from two flaws: first, if there are a lot of integer keys, it does a lot of unnecessary work; and second, if the integer keys are not contiguous, then it will miss some keys which would also be missed by ipairs. This might not be a concern.
```
```
If you want a table which can be iterated separately by numeric or non-numeric keys, I recommend that you use two tables. Something like the following might work for you:
```
do
-- metatable sorts gets and sets by type of key
local meta = {}
function meta:__index(k)
return self.numeric[k] or self.nonnumeric[k]
end
function meta:__newindex(k, v)
if type(k) == "number" then
self.numeric[k] = v
if k > self.n then self.n = k end
else
self.nonnumeric[k] = v
end
end

-- constructor creates the subtables, sets 'n', and
-- assigns the metatable
function SplitTable()
return setmetatable({numeric = {},
nonnumeric = {},
n = 0},
meta)
end

-- two iterators, one for each part
-- The numeric iterator uses our own stored n,
-- but it could have been written more simply
-- with ipairs. The advantage of this one is
-- that it allows vectors with nil's in them.
-- (but doesn't return the keys with nil values)

local function numeric_iter(st, i)
for testkey = i + 1, st.n do
local testval = st.numeric[testkey]
if testval ~= nil then
return testkey, testval
end
end
end

-- we don't attempt to deal with non-positive or
-- non-integer keys. That's left as an exercise
function numerics(st)
return numeric_iter, st, 0
end

-- The nonnumeric iterator just punts to next
function nonnumerics(st)
return next, st.nonnumeric, nil
end
end

-- test code
-- Simple Sieve of Erastothenes, which builds a factorisation
-- table up to its argument.
function sieve(n)
local factored = {}
for prime = 2, math.sqrt(n) do
if not factored[prime] then
for factor = 2, n / prime do
factored[prime * factor] =
string.format("%d x %d", prime, factor)
end
end
end
return factored
end

-- Test the sieve
do
local f = sieve(52)
for i = 2, 52 do
if f[i] then print(string.format("%d = %s", i, f[i]))
else print(string.format("%d is prime", i))
end
end
end

-- Now create a SplitTable whose keys are the primes up to 52
-- and the English vowels

primesAndVowels = SplitTable()
do
local factored = sieve(52)
-- note: ipairs won't work here
for i = 2, 52 do
if not factored[i] then
primesAndVowels[i] = "prime"
end
end
end

for char in string.gfind("aeiouyAEIOUY", "(.)") do
primesAndVowels[char] = "vowel"
end

-- And what do we have?
for k, v in numerics(primesAndVowels) do
print(k, v)
end

for k, v in nonnumerics(primesAndVowels) do
print(k, v)
end

```