2011-09-23 1 views
7

Sto sviluppando una semplice funzione ottimizzata JSON. Lua usa le tabelle per rappresentare gli array ma in JSON ho bisogno di riconoscere tra loro. Il codice di seguito viene utilizzato:Come faccio a sapere se una tabella è una matrice?

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)) 

Come si può vedere in JSON riferimento un object è quello che viene chiamato un table in Lua ed è diverso da un array.

La domanda è come posso rilevare se una tabella viene utilizzato come un array?

  • Una soluzione, naturalmente, è quello di passare attraverso tutte le coppie e vedere se hanno solo i tasti numerici consecutivi ma non è abbastanza veloce.
  • Un'altra soluzione è di mettere un flag nella tabella che dice che è un non matrice un oggetto.

Qualsiasi soluzione più semplice/intelligente?

+0

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

risposta

1

Grazie. Ho sviluppato il seguente codice e funziona:

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

Questo potrebbe essere un po 'più veloce se si tiene traccia delle voci in una metatable, ma non sarebbe generico. Sarebbe, tuttavia, molto più veloce per i grandi tavoli. – tjameson

+0

Sei sicuro? Se l'indice della tabella non è continuo ... – Itachi

+0

Quindi non è un array. – AlexStack

4

No, non esiste un modo integrato per differenziare, perché a Lua non c'è differenza.

Ci librerie JSON, che probabilmente già fanno questo sono già esistente (ad es. Lua CJSON.

Altre opzioni sono

  • lasciarlo fino a all'utente di specificare quale tipo l'argomento è, o come che tipo che avrebbe voluto averlo trattato.
  • hanno array dichiarato esplicitamente disponendo la __newindex tale che solo nuovi indici numerici e le successive possono essere utilizzati.
+0

Mi piace la soluzione __newindex. – AlexStack

5

Se si desidera una soluzione rapida, semplice e non intrusiva che funzioni nella maggior parte dei casi, allora direi semplicemente di controllare l'indice 1 - se esiste, la tabella è un array. Certo, non c'è garanzia, ma nella mia esperienza, le tabelle raramente hanno sia numeri che altre chiavi. Se sia accettabile per te scambiare alcuni oggetti per gli array e se ti aspetti che ciò avvenga spesso dipende dal tuo scenario di utilizzo - immagino che non sia buono per la libreria JSON generale.

Modifica: Per la scienza, sono andato a vedere come Lua CJSON fa le cose. Passa attraverso tutte le coppie e controlla se tutte le chiavi sono numeri interi mantenendo la chiave massima (la funzione rilevante è lua_array_length). Quindi decide se serializzare la tabella come una matrice o un oggetto a seconda di quanto sparsa la tabella è (il rapporto è controllato dall'utente), cioè una tabella con indici 1,2,5,10 verrà probabilmente serializzata come matrice mentre una tabella con gli indici 1,2,1000000 andranno come oggetto. Immagino che questa sia in realtà una soluzione abbastanza buona.

3

@AlexStack

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

Questo codice è sbagliato, se fallisce quando uno dei elemets è false.

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

penso che l'intera espressione può essere modificata per type(t[i]) == nil ma ancora non riuscirà in alcuni scenari, perché non sosterrà i valori nulli.

Un buon modo per andare, credo, sta cercando con ipairs o verificare se #t è pari a count, ma #t restituisce 0 con oggetti e count saranno pari a zero con gli array vuoti, quindi potrebbe essere necessario un controllo in più al inizio della funzione, qualcosa come: if not next(t) then return true.

Come sidenote, io sto incollando un'altra implementazione, si trovano in lua-cjson (da 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 
0

Ho scritto questa funzione per le tabelle graziose stampa lua, e ha dovuto risolvere lo stesso problema . Nessuna delle soluzioni qui rappresenta casi limite come alcune chiavi sono numeri ma altre no. Questo test ogni indice per vedere se è compatibile con l'essere un array.

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

L'algoritmo più semplice per distinguere tra gli array/non-array è questa:

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 

Spiegazione qui: https://web.archive.org/web/20140227143701/http://ericjmritz.name/2014/02/26/lua-is_array/

Detto questo, si avrà ancora problemi con tavoli vuoti - sono loro "matrici" o "hash"?

Per il caso particolare di serializzazione di JSON, quello che faccio è contrassegnare gli array con un campo nel loro 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