Lua: check if table can be looped through ipairs & ipairs starting from 0
I have a nice little Lua parsing that prints out some pretty nice lua code and I love it ... it works nicely. There is a small catch ... if I go to print a table or array that has integer keys in it, it goes through it with pairs
(which won't mess up the code ironically), but I'd rather use ipairs
if possible. So I want to know if the table can be checked (without physically looking at it), if it can use ipairs to loop through it, use pairs first. Then is there a way to start the loop at 0 instead of Lua's default 1?
Lua Table Parser (base code found on google modified it to make it more array friendly) ...
function TableParser(name, object, tabs)
local function serializeKeyForTable(k)
if type(k)=="number" then
return ""
end
if string.find(k,"[^A-z_-]") then
return k
end
return k
end
local function serializeKey(k)
if type(k)=="number" then
if k == 0 then
return "\t[" .. k .."] = "
else
return "\t"
end
end
if string.find(k,"[^A-z_-]") then
return "\t" .. k .. " = "
end
return "\t" .. k .. " = "
end
if not tabs then tabs = "" end
local function serialize(name, object, tabs) -- = {
local output = tabs .. (name ~= "" and name .. " = " or "") .. "{" .. "\n"
for k,v in pairs(object) do
if type(v) == "number" then
output = output .. tabs .. serializeKey(k) .. v
elseif type(v) == "string" then
output = output .. tabs .. serializeKey(k) .. string.format("%q",v)
elseif type(v) == "table" then
output = output .. serialize(serializeKeyForTable(k), v, tabs.."\t")
elseif type(v) == "boolean" then
output = output .. tabs .. serializeKey(k) .. tostring(v)
else
output = output .. tabs .. serializeKey(k) .. "\"" .. tostring(v) .. "\""
end
if next(object,k) then
output = output .. ",\n"
end
end
return output .. "\n" .. tabs .. "}"
end
return serialize(name, object, tabs)
end
source to share
So I want to know if there is a way to inspect the table (without looking at it physically) if it can use ipairs to scroll it, use pairs first.
Don't check, just do it! First, use ipairs
and track the largest key that the iterator has returned ipairs
. Then use pairs
to re-iterate and ignore all integer keys between 1
and this largest key from ipairs
.
If you really want to check that ipairs
it will do something, look at the index 1
on the table ( rawget( object, 1 ) ~= nil
). Checking to see if it will ipairs
span all the items in a table is not possible without repeating the table.
Then is there a way to start the loop at 0 instead of Lua's default 1?
ipairs(t)
returns three values: an iterator function, a table t
as a state variable, and an initial index value 0
. If you are using -1
an index as the initial value, ipairs
will start iterating in 0
(the iterator function always increments by one before using the index value):
t = { 1, 2, 3, [ 0 ] = 0 }
for i,v in ipairs( t ), t, -1 do -- only use first value returned by ipairs
print( i, v )
end
However, keep in mind that Lua 5.2 added support for a new metamethod __ipairs
that allows you to return a custom triplet iterator to use for ipairs
-iteration, and the iterator function returned in this case may require different state and initial index values.
Edit:
To include suggestions in your code insert before for k,v in pairs(object) do
-loop:
local largest = 0
for k,v in ipairs(object) do
largest = k
local t = type(v)
if t == "table" then
output = output .. tabs .. "\t" .. serialize( "", v, tabs.."\t" )
elseif t == "string" then
output = output .. tabs .. "\t" .. string.format("%q", v)
else
output = output .. tabs .. "\t" .. tostring(v)
end
output = output .. ",\n"
end
and inside the loop add an extra operator if
to check the keys of the array:
for k,v in pairs(object) do
if type(k) ~= "number" or k < 1 or k > largest or math.floor(k) ~= k then
-- if type(v) == "number" then
-- ...
end
end
If you apply this modified function TableParser
to the following table:
local t = {
1, 2, 3,
value = "x",
tab = {
"a", "b", field = "y"
}
}
print( TableParser( "", t ) )
output:
{
1,
2,
3,
tab = {
"a",
"b",
field = "y"
},
value = "x"
}
But serializing tables correctly is tricky. For example. your implementation does not treat loops or tables as keys. See the Lua Wiki for some implementations.
source to share
You can always iterate over the table both with pairs
and ipairs
whether it makes sense or not.
-
ipairs
iterates over the sequence present in the array (which means consecutive integer keys starting at 1 up to the first missing value), unless overridden by metamethod__ipairs
(5.2). -
pairs
iterates over all key-value pairs usingnext
(thus in an unspecified order), unless overridden by metamet__pairs
(5.2).
This means that ipairs
generally not listing any key-value pair pairs
will not be displayed.
And there is no way to check if it will ipairs
enumerate all keys pairs
, enumerate but enumerate all and test manually.
BTW: You can create your own iterator that iterates over the sequence first and then on top of everything else:
function my_iter(t)
local k, cap
return function()
local v
if k == nil then k, cap = 0 end
if not cap then
k = k + 1
v = t[k]
if v ~= nil then return k, v end
cap, k = k
end
repeat k, v = next(k)
until type(k) ~= "number" or 0 < k and k < cap and math.ceil(k) == k
return k, v
end
end
Though it's probably better to just sort the keys for a pretty-printable:
function sorted_iter(t)
local keys, index = {}, 0
for k in next, t do
keys[#keys + 1] = k
end
table.sort(keys)
return function()
index = index + 1
local k = keys[index]
return k, t[k]
end
end
source to share