Как проверить, существует ли подполе таблицы в Lua?

Я пытаюсь использовать модели Lua Викиданных.

Мне нужно найти определенный идентификатор в объектах Викиданных:

    subjectitemofthisproperty = 'Q' .. tostring( entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] )

Основная проблема заключается в том, что у некоторых сущностей нет подполя entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"].

Чтобы Lua вернул:

Ошибка Lua в модуле: LoPwS_row в строке 80: попытка проиндексировать поле «P1629» (нулевое значение).

If I do a :

if entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] ~= nil then

Это не сработает, потому что условие вызывает поле, а затем возвращает ту же ошибку.

Есть ли простое решение для проверки существования поля? Спасибо!


person Xiiryo    schedule 03.11.2018    source источник
comment
он показывает, что ['P1629'] не существует.   -  person wsha    schedule 03.11.2018
comment
Точно. У некоторых сущностей он есть, у некоторых нет.   -  person Xiiryo    schedule 04.11.2018


Ответы (2)


Вы можете написать простую функцию, которая возвращает nil, если где-то в цепочке таблиц есть nil. Назовем его lookup:

function lookup(t, ...)
    for _, k in ipairs{...} do
        t = t[k]
        if not t then
            return nil
        end
    end
    return t
end

-- Test it
t = {a = {b = {c = 5}}}
lookup(t, 'a', 'x', 'b') -- Returns nil
lookup(t, 'a', 'b', 'c') -- Returns 5
person Oleg Andriyanov    schedule 03.11.2018
comment
Спасибо. Содержимое entity['claims'] должно быть entity['claims']['P1629'] или nil, чтобы оно работало, не так ли? - person Xiiryo; 04.11.2018
comment
@Thibd Я не понял твоего вопроса. Все значения на пути к запрошенному значению должны быть таблицами (или другими индексируемыми объектами) или nils. Но вы можете добавить дополнительные проверки типа объекта на каждой итерации. - person Oleg Andriyanov; 04.11.2018
comment
Хорошо, после некоторого теста я понял: вызов mytable[nonexstingkey] возвращает nil, а не ошибку, как я сначала пытался. Это вызов mytable[nonexistingkey][nonexistingsubkey], который генерирует ошибку. - person Xiiryo; 04.11.2018
comment
Решено. Большое спасибо. - person Xiiryo; 04.11.2018

Вы можете решить эту проблему с помощью прокси-метатабельного и сомнительного шаблона Null Object. Нулевой объект может выглядеть так:

local Null = {}
local NullProto = { __index = function(t,k) return Null end }
setmetatable(Null, NullProto)

Null всегда будет возвращаться, когда вы пытаетесь его проиндексировать.

Ключевая идея решения заключается в создании прокси-объекта для исходной таблицы, который будет использовать следующую логику:

  • Если какой-то ключ не существует в исходной таблице, верните Null Object
  • If some key exists in the original table
    • If referenced value is of primitive type then return the value
    • Если ссылочное значение имеет табличный тип, оберните его прокси и верните

Код может выглядеть так

 function make_safe_table(nonsafe)
    local proto = {
    __index = function(t, k)
        local val = nonsafe[k]
        if val == nil then
            return Null
        elseif type(val) == 'table' then
            return make_safe_table(val)
        else
            return val
        end
    end
    }
    return setmetatable({}, proto)
end

Вы можете использовать эту функцию следующим образом:

local original = {
    nested = {
        deep = { hidden = 'value'}
    },
    simple = 'simple',
    [3] = 'third'
}
local safe_original = make_safe_table(original)
print(safe_original.not_exists == Null) -- true
print(safe_original.nested.not_exists == Null) -- true
print(safe_original.nested.deep.not_exists == Null) -- true
print(safe_original.not_exists.still_not_exists == Null) -- true
print(safe_original.nested.deep.hidden) -- 'value'
print(safe_original.simple) -- 'simple'
print(safe_original[3]) -- 'third'

Я бы не рекомендовал вам использовать этот код в производственной среде, потому что он не был должным образом протестирован, но я надеюсь, что он поможет вам создать надежное решение. Подробнее см. https://www.lua.org/pil/13.4.html. подробности о метатаблицах.

person Danilа Maslennikov    schedule 03.11.2018
comment
Я думаю, вы имели в виду, что local proto = { должна быть первой строкой функции make_safe_table, так как глобальная proto не имеет смысла - person Oleg Andriyanov; 03.11.2018