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

      

+3


source to share


2 answers


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.

+6


source


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 using next

    (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

      

+5


source







All Articles