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 

Tags:

Lua