2011-09-23 143 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