How do I know if a table is an array?
No there is no built-in way to differentiate, because in Lua there is no difference.
There are already existing JSON libraries, which probably already do this (eg. Lua CJSON.
Other options are
- leave it up to the user to specify what type the argument is, or as what type he'd want to have it treated.
- have arrays explicitly declared by arranging the
__newindex
such that only new numerical and subsequent indices are allowed to be used.
If you want fast, simple, non-intrusive solution that will work most of the times, then I'd say just check index 1 - if it exists, the table is an array. Sure, there's no guarantee, but in my experience, tables rarely have both numerical and other keys. Whether it's acceptable for you to mistake some objects for arrays and whether you expect this to happen often depend on your usage scenario - I guess it's not good for general JSON library.
Edit: For science, I went to see how Lua CJSON does things. It goes through all pairs and checks if all keys are integers while keeping the maximum key (the relevant function is lua_array_length
). Then it decides whether to serialize the table as an array or object depending on how sparse the table is (the ratio is user controlled) i.e. a table with indices 1,2,5,10 will probably be serialized as an array while a table with indices 1,2,1000000 will go as an object. I guess this is actually quite good solution.
The simplest algorithm to differentiate between arrays/non-arrays is this one:
local function is_array(t)
local i = 0
for _ in pairs(t) do
i = i + 1
if t[i] == nil then return false end
end
return true
end
Explanation here: https://web.archive.org/web/20140227143701/http://ericjmritz.name/2014/02/26/lua-is_array/
That said, you will still have issues with empty tables - are they "arrays" or "hashes"?
For the particular case of serializing json, what I do is marking arrays with a field in their metatable.
-- use this when deserializing
local function mark_as_array(t)
setmetatable(t, {__isarray = true})
end
-- use this when serializing
local function is_array(t)
local mt = getmetatable(t)
return mt.__isarray
end
@AlexStack
if not t[i] and type(t[i])~="nil" then return false end
This code is wrong, if fails when one of the elemets is false
.
> return isArray({"one", "two"})
true
> return isArray({false, true})
false
I think the whole expression can be changed to type(t[i]) == nil
but it still will fail in some scenarios because it will not support nil values.
A good way to go, I think, is trying with ipairs
or checking whether #t
is equal to count
, but #t
returns 0 with objects and count
will be zero with empty arrays, so it may need an extra check at the beginning of the function, something like: if not next(t) then return true
.
As a sidenote, I'm pasting another implementation, found in lua-cjson (by Mark Pulford):
-- Determine with a Lua table can be treated as an array.
-- Explicitly returns "not an array" for very sparse arrays.
-- Returns:
-- -1 Not an array
-- 0 Empty table
-- >0 Highest index in the array
local function is_array(table)
local max = 0
local count = 0
for k, v in pairs(table) do
if type(k) == "number" then
if k > max then max = k end
count = count + 1
else
return -1
end
end
if max > count * 2 then
return -1
end
return max
end