2011-09-23 140 views
7

我正在开发一个简单优化的JSON函数。 Lua使用表来表示数组,但是在JSON中,我需要在它们之间进行识别。下面的代码用于:如何知道表是否是数组?

t={ 
    a="hi", 
    b=100 
} 

function table2json(t,formatted) 
if type(t)~="table" then return nil,"Parameter is not a table. It is: "..type(t) end 

local ret=""--return value 
local lvl=0 --indentation level 
local INDENT=" " --OPTION: the characters put in front of every line for indentation 
function addToRet(str) if formatted then ret=ret..string.rep(INDENT,lvl)..str.."\n" else ret=ret..str end end 

addToRet("{") 
lvl=1 
for k,v in pairs(t) do 
    local typeof=type(v) 
    if typeof=="string" then 
     addToRet(k..":\""..v.."\"") 
    elseif typeof=="number" then 
     addToRet(k..":"..v) 
    end 
end 
lvl=0 
addToRet("}") 

return ret 
end 

print(table2json(t,true)) 

正如你可以看到JSON引用object是所谓在Lua一个table,它是从array不同。

问题是我如何检测表是否被用作数组?

  • 当然,一个解决方案是通过所有对,看看他们是否只有数字连续键,但这不够快。
  • 另一种解决方案是在表中放置一个标志,表示它是一个数组而不是对象。

任何更简单/更智能的解决方案?

+0

参见:http://stackoverflow.com/questions/6077006/how-can-i-check-if-a-lua-table-contains-only-sequential-numeric-indices/6080274#6080274 – BMitch

回答

1

谢谢。我公司开发的下面的代码和它的作品:

---Checks if a table is used as an array. That is: the keys start with one and are sequential numbers 
-- @param t table 
-- @return nil,error string if t is not a table 
-- @return true/false if t is an array/isn't an array 
-- NOTE: it returns true for an empty table 
function isArray(t) 
    if type(t)~="table" then return nil,"Argument is not a table! It is: "..type(t) end 
    --check if all the table keys are numerical and count their number 
    local count=0 
    for k,v in pairs(t) do 
     if type(k)~="number" then return false else count=count+1 end 
    end 
    --all keys are numerical. now let's see if they are sequential and start with 1 
    for i=1,count do 
     --Hint: the VALUE might be "nil", in that case "not t[i]" isn't enough, that's why we check the type 
     if not t[i] and type(t[i])~="nil" then return false end 
    end 
    return true 
end 
+0

如果您跟踪metatable中的条目,这可能会更快一些,但它不是通用的。但是,对于大型表格,它会更快。 – tjameson

+0

你确定吗?如果表格的索引不连续... – Itachi

+0

那么它不是一个数组。 – AlexStack

4

不,没有内在的区分方法,因为在Lua中没有区别。

有已经存在的JSON库,这可能已经做到这一点(如:Lua CJSON

其他选项是

  • 把它留给用户指定什么类型的参数,或者作为什么类型的,他会想有它处理。
  • 已阵列明确安排__newindex这样,只有新的数值,并随后指数允许使用。
声明
+0

我喜欢__newindex解决方案。 – AlexStack

5

如果你想快速,简单,非侵入性的解决方案,将工作大多数的时代,那么我会说只是检查索引1 - 如果它存在,表是一个数组。当然,没有保证,但根据我的经验,表格很少有数字键和其他键。你是否可以将某些对象误认为是数组,以及是否期望这种情况发生,这通常取决于你的使用场景 - 我想这对于一般的JSON库来说并不好。

编辑:为了科学,我去看看Lua CJSON是如何做的。它会遍历所有对并检查所有键是否为整数,同时保持最大键(相关函数为lua_array_length)。然后它决定是否将数组序列化为数组或对象,具体取决于表的稀疏程度(比例是由用户控制的),即具有索引1,2,5,10的表可能会被序列化为数组,而表指数1,2,100,000将作为一个对象。我想这实际上是相当好的解决方案。

3

@AlexStack

if not t[i] and type(t[i])~="nil" then return false end

这段代码是错误的,如果在elemets之一是false失败。

> return isArray({"one", "two"}) 
true 
> return isArray({false, true}) 
false 

我觉得整个表达式可以改变type(t[i]) == nil,但它仍然会在某些情况下会失败,因为它不支持零值。

一个很好的路要走,我想,是试图用ipairs或检查#t是否等于count,但#t返回0的对象和count将是零空数组,所以它可能需要在额外的检查函数的开头,如:if not next(t) then return true

一点题外话,我粘贴另一种实现方式,在LUA-cjson发现(马克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 
0

我写了这个功能相当印刷LUA表,必须解决同样的问题。这里的解决方案没有一个解释边缘情况,例如某些键是数字,但其他键不是。这将测试每个索引以查看它是否与数组兼容。

function pp(thing) 
    if type(thing) == "table" then 
     local strTable = {} 
     local iTable = {} 
     local iterable = true 
     for k, v in pairs(thing) do 
      --if the key is a string, we don't need to do "[key]" 
      local key = (((not (type(k) == "string")) and "["..pp(k).."]") or k) 
      --this tests if the index is compatible with being an array 
      if (not (type(k) == "number")) or (k > #thing) or(k < 1) or not (math.floor(k) == k) then 
       iterable = false 
      end 
      local val = pp(v) 
      if iterable then iTable[k] = val end 
      table.insert(strTable, (key.."="..val)) 
     end 
     if iterable then strTable = iTable end 
     return string.format("{%s}", table.concat(strTable,",")) 
    elseif type(thing) == "string" then 
     return '"'..thing..'"' 
    else 
     return tostring(thing) 
    end 
end 
3

最简单的算法阵列/非阵列之间的区别是这一个:

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 

说明这里:https://web.archive.org/web/20140227143701/http://ericjmritz.name/2014/02/26/lua-is_array/

尽管如此,你仍然会与空表的问题 - 是他们“数组”或“哈希”?

对于序列化json的特殊情况,我所做的是在它们的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