Как узнать, является ли таблица массивом? - PullRequest
10 голосов
/ 23 сентября 2011

Я разрабатываю простую оптимизированную 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.

.

Вопрос в том, как я могу определить, используется ли таблица в качестве массива?

  • Одно из решений, конечно, состоит в том, чтобы просмотреть все пары и посмотреть, есть ли у них только числовые последовательные клавиши, но этого недостаточно.
  • Другое решение - поместить в таблицу флаг, который говорит, что это массив, а не объект.

Есть ли более простое / разумное решение?

Ответы [ 7 ]

8 голосов
/ 23 сентября 2011

Если вам нужно быстрое, простое, ненавязчивое решение, которое будет работать в большинстве случаев раз, тогда я бы сказал, просто проверьте индекс 1 - если он существует, таблица является массивом.Конечно, нет никакой гарантии, но по моему опыту, таблицы редко имеют как цифровые, так и другие ключи.Приемлемо ли для вас ошибочно принимать некоторые объекты за массивы и ожидаете ли вы, что это часто случается, зависит от вашего сценария использования - я думаю, что это нехорошо для общей библиотеки JSON.

Редактировать: Для науки я пошел посмотретькак Lua CJSON делает вещиОн просматривает все пары и проверяет, являются ли все ключи целыми числами, сохраняя при этом максимальный ключ (соответствующая функция - lua_array_length).Затем он решает, следует ли сериализовать таблицу в виде массива или объекта, в зависимости от того, насколько разреженной является таблица (это соотношение контролируется пользователем), то есть таблица с индексами 1,2,5,10, вероятно, будет сериализована в виде массива, а таблица синдексы 1,2,1000000 пойдут как объект.Полагаю, это действительно неплохое решение.

4 голосов
/ 23 сентября 2011

Нет, нет встроенного способа дифференциации, потому что в Lua нет разницы.

Уже существуют библиотеки JSON, которые, вероятно, уже делают это (например, Lua CJSON .

Другие варианты:

  • . Пользователь может указать, какой тип аргумента или какой тип он хочет обработать.
  • имеют массивы, явно объявленные путем упорядочения __newindex таким образом, что разрешается использовать только новые числовые и последующие индексы.
3 голосов
/ 07 сентября 2014

Простейший алгоритм различения массивов / не массивов следующий:

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 я отмечаю массивы полем в их метатабельных элементах.

-- 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
3 голосов
/ 07 января 2014

@ AlexStack

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

Этот код неверен, если происходит сбой, когда один из элементов имеет значение 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 (Марком Пулфордом):

-- 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 
2 голосов
/ 08 октября 2018

Вот более простая проверка, основанная на специфическом для Lua механизме функции #len.

function is_array(table)
  if type(table) ~= 'table' then
    return false
  end

  -- objects always return empty size
  if #table > 0 then
    return true
  end

  -- only object can have empty length with elements inside
  for k, v in pairs(table) do
    return false
  end

  -- if no elements it can be array and not at same time
  return true
end

local a = {} -- true
local b = { 1, 2, 3 } -- true
local c = { a = 1, b = 1, c = 1 } -- false
1 голос
/ 23 сентября 2011

Спасибо.Я разработал следующий код, и он работает:

---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 голосов
/ 06 сентября 2014

Я написал эту функцию для красивой печати таблиц 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...