通过路径写入动态多维 table

Writing to Dynamic Multidimensional table via path

开始

我的数据结构像

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

我在上面添加了以下元数据table

  __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}]

这对于获取数据非常有用,但是当我尝试设置数据时,它反而会创建一个新的 table。例如data[{2,3,1}] = "Updated Stuff"将数据变成

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

虽然如果我然后调用 print(data[{2,3,1}] 我得到正确的值但是如果我然后尝试使用 ipairs 递归循环遍历 table 我得到原始值并且忽略额外的值。我知道从 Lua 的意义上来说这应该是可能的,但就是只见树木不见森林。

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

print(data[{2,3,1}]) 索引数据并因此调用您的 __index 元方法,然后 returns 字符串 "deeper stuff"

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

由于您没有 __newindex,您只是使用 table {2,3,1} 作为 table 键进行常规分配。

如果您想使用 table 的内容作为索引 data[2][3][1].

的键,您必须实施 __newindex

我不确定你为什么更喜欢 data[{2,3,1}] 而不是 data[2][3][1]。你在增加不必要的混乱。在 table 中提供 table 键会很方便的少数情况可以使用一个同时接受 table 和键列表的简单函数来解决。但这只是我个人的意见。 我刚刚向您展示了我最后一个答案的可能性。这并不意味着这样做是个好主意 :) 抱歉,如果不清楚。

您可以简单地使用两个函数:

updateDeep(t, newVal, ...)getDeep(t, ...) 可以类似于 __index 元方法实现。将 k 替换为 {...}

您还应该实现 __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}] = "updated stuff #1"
print(data[{2,3,1}])
data[{3,1,2}] = "updated stuff #2"
print(data[{3,1,2}])