通过路径写入动态多维表

接着 参考动态多维表

我有一个像下面这样的数据结构

local data = {
  [1] = {
     [1] = { "stuff" },
  },
  [2] = {
     [1] = { "stuff" },
     [2] = { "more stuff" },
     [3] = {
        [1] = "deeper stuff"
     }
  }
}

我向其中添加了以下元表

  __index = function(t, k)
    for i,v in ipairs(k) do
      if not t then error("attempt to index nil") end
      t = rawget(t, v)
    end
    return t
  end
  })

  print(data[{2,3,1}]

这对于获取数据来说很方便,但是当我尝试设置数据时,它却创建了一个新表。例如,data[{2,3,1}] = "Updated Stuff" 将 data 变成

  [1] = {
     [1] = { "stuff" }
   },
  [2] = {
     [1] = { "stuff" },
     [2] = { "more stuff" },
     [3] = {
        [1] = "deeper stuff"
     }
  },
  ["table: 000001A7E014F570"] = "Updated Stuff"
}

尽管如果我接着调用 print(data[{2,3,1}] 我可以得到正确的值,但是如果我尝试使用 ipairs 递归循环遍历表,则会忽略额外的值。我知道从 Lua 的角度来看,这应该是可能的,但是我只能看到树木而看不到森林。

我想我在寻找 Lua 版的 lodash 的 _.set(table, "path.to.key", value)

点赞
用户2858170
用户2858170

print(data[{2,3,1}]) 使用索引操作调用 __index 元方法,接着返回字符串 "deeper stuff"

data[{2,3,1}] = "updated stuff" 是赋值索引操作,它会调用 __newindex 而非 __index

由于你没有实现 __newindex,所以你只是简单地使用 {2,3,1} 表作为表键进行常规赋值操作。

如果你想使用表的内容作为键来索引 data[2][3][1],就必须实现 __newindex

我不确定你为什么会更喜欢 data[{2,3,1}] 而非 data[2][3][1]。这只会增加混乱。极少数可以使用在表中提供表键的情况可以使用一个简单的函数来解决。但这只是我的个人意见。

我只是在上一个答案中展示了什么是可能的,这并不意味着这是一个好主意 :) 如果这不清楚,请原谅。

你可以简单地拥有两个函数:updateDeep(t, newVal, ...)getDeep(t, ...),它们可以类似于 __index 元方法实现。将 k 替换为 {...}

2021-06-02 09:07:02
用户1847592
用户1847592

你应该也实现 __newindex 元方法

local data = {
   [1] = {
       [1] = { "stuff" },
   },
   [2] = {
      [1] = { "stuff" },
      [2] = { "more stuff" },
      [3] = {
         [1] = "deeper stuff"
      }
   }
}

setmetatable(data, {
   __index = function(t, k)
      for i, k in ipairs(k) do
         if t == nil then return nil end
         if type(t) ~= "table" then error("Unexpected subtable", 2) end
         t = rawget(t, k)
      end
      return t
   end,
   __newindex = function(t, k, v)
      local last_k
      for i, k in ipairs(k) do
         k, last_k = last_k, k
         if k ~= nil then
            local parent_t = t
            t = rawget(parent_t, k)
            if t == nil then
               t = {}
               rawset(parent_t, k, t)
            end
            if type(t) ~= "table" then error("Unexpected subtable", 2) end
         end
      end
      rawset(t, last_k, v)
   end
})

print(data[{2,3,1}])
data[{2,3,1}] = "更新的东西 #1"
print(data[{2,3,1}])
data[{3,1,2}] = "更新的东西 #2"
print(data[{3,1,2}])
2021-06-02 10:36:10