检查Lua表的成员是否存在于任何级别

我需要检查一个成员是否存在于一个不在下一级,而是沿着成员路径的表中。

foo = {}
if foo.bar.joe then
  print(foo.bar.joe)
end

这会造成一个 attempt to index field 'bar' (a nil value) 错误,因为 bar 没有定义。

我的通常解决方案是逐个测试链条。

foo = {}
if foo.bar and foo.bar.joe then
  print(foo.bar.joe)
end

但是当有许多嵌套的表格时,这可能会非常繁琐。是否有比逐个进行测试更好的方法?

点赞
用户4962485
用户4962485

我不明白你所说的“沿着成员路径”的意思。从例子中看,我猜想你是想在一个“子表”中查找一个值?

local function search(master, target) --target is a string
    for k,v in next, master do
        if type(v)=="table" and v[target] then return true end
    end
end

举一个简单的例子。如果您使用这样的函数,您可以传递 foo 表和 joe 字符串来查看 foo.*.joe 是否存在。希望这对您有所帮助。

2015-06-07 04:17:49
用户1847592
用户1847592
```lua
debug.setmetatable(nil, {__index = {}})

foo = {}
print(foo.bar.baz.quux)
print(({}).prd.krt.skrz.drn.zprv.zhlt.hrst.zrn)  -- sorry ))

将以上 Lua 代码翻译成中文并且保留原本的 markdown 格式:

debug.setmetatable(nil, {__index = {}})

foo = {}
print(foo.bar.baz.quux)
print(({}).prd.krt.skrz.drn.zprv.zhlt.hrst.zrn)  -- 对不起 ))
2015-06-07 09:16:47
用户1288130
用户1288130

我认为你正在寻找以下代码:

local function get(Obj, Field, ...)
    if Obj == nil or Field == nil then
        return Obj
    else
        return get(Obj[Field], ...)
    end
end

local foo = {x = {y = 7}}
assert(get() == nil)
assert(get(foo) == foo)
assert(get(foo, "x") == foo.x)
assert(get(foo, "x", "y") == 7)
assert(get(foo, "x", "z") == nil)
assert(get(foo, "bar", "joe") == nil)
assert(get(foo, "x", "y") or 41 == 7)
assert(get(foo, "bar", "joe") or 41 == 41)
local Path = {foo, "x", "y"}
assert(get(table.unpack(Path)) == 7)

get 函数简单地遍历给定的路径,直到遇到 nil 值为止。看起来很完美。当然你可以考虑一个比 “get” 更好的函数名。

or 连用时要当心。

我很佩服 Egor 聪明的答案,但一般来说,我们不应该依赖这种技巧。

参见

2015-06-07 17:33:42
用户3735873
用户3735873

如果我正确理解了您的问题,这里有一个可能性:

function isField(s)
  local t
  for key in s:gmatch('[^.]+') do
    if t == nil then
      if _ENV[ key ] == nil then return false end
      t = _ENV[ key ]
    else
      if t[ key ] == nil then return false end
      t = t[ key ]
    end
    --print(key) --for DEBUGGING
  end
  return true
end

-- To test

t = {}
t.a = {}
t.a.b = {}
t.a.b.c = 'Found me'

if isField('t.a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
if isField('t.a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end

更新:根据 cauterite 的建议,这里有一个版本,它也可以处理局部变量,但需要传入两个参数 :(

function isField(t,s)
  if t == nil then return false end
  local t = t
  for key in s:gmatch('[^.]+') do
    if t[ key ] == nil then return false end
    t = t[ key ]
  end
  return true
end

-- To test

local
t = {}
t.a = {}
t.a.b = {}
t.a.b.c = 'Found me'

if isField(t,'a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
if isField(t,'a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end
2015-06-07 20:14:36
用户4988149
用户4988149
foo = {}

foo.boo = {}

foo.boo.jeo = {}

foo.boo.joe is foo['boo']['joe'] and so

i make next function

function exist(t)

    local words = {}
    local command

    for i,v in string.gmatch(t, '%w+') do words[#words+1] = i end

    command = string.format('a = %s', words[1])

    loadstring(command)()

    if a == nil then return false end

    for count=2, #words do
        a = a[words[count]]
        if a == nil then return false end
    end

    a = nil
    return true
end

foo = {}
foo.boo = {}
foo.boo.joe = {}

print(exist('foo.boo.joe.b.a'))

使用 loadstring 函数创建临时变量。我的 Lua 版本是 5.1。

在 5.2 或 5.3 中移除 loadstring 函数,改用 load

2015-06-13 09:45:04
用户1878009
用户1878009

要搜索表中在任意层级上的元素,我会使用以下的方法:

function exists(tab, element)
    local v
    for _, v in pairs(tab) do
        if v == element then
            return true
        elseif type(v) == "table" then
            return exists(v, element)
        end
    end
    return false
end

testTable = {{"胡萝卜", {"蘑菇", "生菜"}, "蛋黄酱"}, "肉桂"}
print(exists(testTable, "蘑菇")) -- true
print(exists(testTable, "苹果")) -- false
print(exists(testTable, "肉桂")) -- true
2015-06-14 06:01:11