2011-09-23 22 views
7

Tôi đang phát triển chức năng JSON được tối ưu hóa đơn giản. Lua sử dụng các bảng để biểu diễn mảng nhưng trong JSON tôi cần phải nhận ra giữa chúng. Đoạn code dưới đây được sử dụng:Làm cách nào để biết bảng có phải là mảng không?

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

Như bạn có thể thấy trong JSON tham khảo một object là những gì được gọi là một table trong Lua và nó khác với một array.

Câu hỏi đặt ra là làm thế nào tôi có thể phát hiện nếu một bảng đang được sử dụng như một mảng?

  • Một giải pháp tất nhiên là trải qua tất cả các cặp và xem liệu chúng có các phím số liên tiếp hay không nhưng không đủ nhanh.
  • Một giải pháp khác là đặt cờ trong bảng cho biết đó là một mảng không phải là đối tượng.

Bất kỳ giải pháp đơn giản/thông minh hơn nào?

+0

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

Trả lời

1

Cảm ơn. Tôi đã phát triển mã sau và nó hoạt động:

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

Điều này có thể nhanh hơn một chút nếu bạn theo dõi các mục nhập trong một metatable, nhưng nó sẽ không được chung chung. Tuy nhiên, nó sẽ nhanh hơn nhiều đối với các bảng lớn. – tjameson

+0

Bạn có chắc chắn không? Nếu chỉ mục của bảng không liên tục ... – Itachi

+0

Sau đó, nó không phải là một mảng. – AlexStack

4

Không có cách nào tích hợp để phân biệt, bởi vì ở Lua không có sự khác biệt.

Hiện đã có thư viện JSON, mà có lẽ đã làm điều này (ví dụ. Lua CJSON.

tùy chọn khác là

  • để nó lên cho người sử dụng để xác định những gì gõ luận là, hoặc như
  • có mảng được khai báo rõ ràng bằng cách sắp xếp __newindex sao cho chỉ sử dụng các chỉ số mới và số tiếp theo mới được sử dụng
+0

Tôi thích giải pháp __newindex. – AlexStack

5

Nếu bạn muốn giải pháp nhanh, đơn giản, không xâm nhập sẽ làm việc nhất của thời gian, sau đó tôi chỉ cần kiểm tra chỉ mục 1 - nếu nó tồn tại, bảng là một mảng. Chắc chắn, không có bảo đảm, nhưng theo kinh nghiệm của tôi, các bảng hiếm khi có cả số và các phím khác. Cho dù đó là chấp nhận được để bạn nhầm lẫn một số đối tượng cho mảng và cho dù bạn mong đợi điều này xảy ra thường phụ thuộc vào kịch bản sử dụng của bạn - tôi đoán nó không tốt cho thư viện JSON chung.

Chỉnh sửa: Đối với khoa học, tôi đã đi xem Lua CJSON hoạt động như thế nào. Nó đi qua tất cả các cặp và kiểm tra nếu tất cả các phím là số nguyên trong khi vẫn giữ phím tối đa (chức năng liên quan là lua_array_length). Sau đó, nó quyết định có tuần tự hóa bảng như là một mảng hay đối tượng tùy thuộc vào mức độ thưa thớt của bảng (tỷ lệ được người dùng kiểm soát) tức là một bảng có chỉ số 1,2,5,10 có thể được sắp xếp thành một mảng trong khi một bảng có chỉ số 1,2,1000000 sẽ đi như một đối tượng. Tôi đoán đây thực sự là giải pháp khá tốt.

3

@AlexStack

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

Mã này là sai, nếu không thành công khi một trong những Elemets là false.

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

Tôi nghĩ rằng toàn bộ biểu thức có thể được thay đổi để type(t[i]) == nil nhưng nó vẫn sẽ thất bại trong một số kịch bản bởi vì nó sẽ không hỗ trợ các giá trị nil.

Một cách tốt để đi, tôi nghĩ rằng, đang cố gắng với ipairs hoặc kiểm tra xem #t bằng count, nhưng #t trả về 0 với các đối tượng và count sẽ zero với mảng trống rỗng, vì vậy nó có thể cần một kiểm tra thêm tại bắt đầu chức năng, chẳng hạn như: if not next(t) then return true.

Là một sidenote, Tôi đang dán khác thực hiện, tìm thấy trong lua-cjson (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

tôi đã viết chức năng này cho các bảng in lua đẹp, và phải giải quyết cùng một vấn đề . Không có giải pháp nào ở đây giải thích cho các trường hợp cạnh như một số khóa là số nhưng không phải là số khác. Điều này kiểm tra mọi chỉ mục để xem nó có tương thích với một mảng hay không.

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

Thuật toán đơn giản nhất để phân biệt giữa các mảng/phi mảng này là một:

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 

Giải thích ở đây: https://web.archive.org/web/20140227143701/http://ericjmritz.name/2014/02/26/lua-is_array/

Điều đó nói rằng, bạn vẫn sẽ có vấn đề với các bảng trống - là họ "mảng" hoặc "băm"?

Đối với trường hợp cụ thể của tuần tự hóa json, những gì tôi làm là đánh dấu các mảng với một trường trong metatable của chúng.

-- 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 
Các vấn đề liên quan