如何从数组中删除所有的“nil”值?

3

我有一个对象数组(或仅为数字),我还有另一个数组,其中包含不应在任何情况下从第一个数组中删除的所有对象。它看起来像这样:

-- Array of objects (just numbers for now)
Objects = {}

-- Array of objects that should always stay in the 'Objects' array
DontDestroyThese = {}

-- Populate the arrays
Objects[#Objects+1] = 1
Objects[#Objects+1] = 2
Objects[#Objects+1] = 3
Objects[#Objects+1] = 4
Objects[#Objects+1] = 5

DontDestroyThese[#DontDestroyThese+1] = 2
DontDestroyThese[#DontDestroyThese+1] = 5

我有一个叫做destroy()的方法,应该从Objects数组中移除所有对象,但不包括DontDestroyThese数组中的对象。该方法大致如下:

function destroy()
    for I = 1, #Objects do
        if(DontDestroyThese[Objects[I]] ~= nil) then
            print("Skipping " .. Objects[I])
        else
            Objects[I] = nil
        end
    end
end

然而,结果是 Objects 数组现在包含了零零散散的 nil 值。我想要删除这些 nil,使得 Objects 数组仅包含在调用 destroy() 后剩下的数字。我该怎么做?

1
你尝试过 table.remove(Objects, I) 吗? - greatwolf
2
Objects[#Objects] = 1 并不会像你想象的那样运行,因为在 Lua 中数组的索引从 1 开始。请尝试使用 Objects[#Objects+1] = 1 - lhf
我同意@lhf的评论。对于DontDestroyThese[#DontDestroyThese]也是一样 - 你必须添加一个+1 - kikito
那个缺失的 +1 是我在输入代码示例时犯的错误。 - manabreak
@manabreak,你使用了错误的术语。对象不包含“这里和这里”的nil。它们包含空洞,因为在Lua赋值中,将nil分配给table[key]意味着“删除该键”。 - Alexander Altshuler
@AlexanderAltshuler,我确实传达了我的信息,所以这并不重要。 - manabreak
3个回答

6

我认为解决方案要简单得多。要删除任何nil(在数组中的“空洞”),您只需要使用pairs()迭代表格即可。这将跳过任何nil,仅返回添加到新本地表中的非nil值,并在“cleanup”函数结束时返回。数组(索引从1..n的表)将保持相同顺序。例如:

function CleanNils(t)
  local ans = {}
  for _,v in pairs(t) do
    ans[ #ans+1 ] = v
  end
  return ans
end

那么你只需要这样做:
Objects = CleanNils(Objects)

测试它:

function show(t)
  for _,v in ipairs(t) do
    print(v)
  end
  print(('='):rep(20))
end

t = {'a','b','c','d','e','f'}
t[4] = nil          --create a 'hole' at 'd'
show(t)             --> a b c
t = CleanNils(t)    --remove the 'hole'
show(t)             --> a b c e f

但是“pairs”以任意顺序迭代。 - Rudolf Adamkovič

5

最有效的方法可能是创建一个新的表来存储结果。尝试在数组中移动值可能比简单地附加到新表中具有更高的开销:

function destroy()
    local tbl = {}
    for I = 1, #Objects do
        if(DontDestroyThese[Objects[I]] ~= nil) then
            table.insert(tbl, Objects[I])
        end
    end
    Objects = tbl
end

这个方法还意味着您不必处理正在迭代的表格/数组的内容。

谢谢你,不知怎么的,这些过于简单的解决方案总是让我无法理解。 :) - manabreak

0
local function remove(t, pred)
  for i = #t, 1, -1 do
    if pred(t[i], i) then
      table.remove(t, i)
    end
  end
  return t
end

local function even(v)
  return math.mod(v, 2) == 0
end

-- remove only even numbers
local t = remove({1, 2, 3, 4}, even)

-- remove values you want
local function keep(t)
  return function(v)
    return not t[v]
  end
end

remove(Objects, keep(DontDestroyThese))

请注意,remove函数的时间复杂度为Θ(n),因此这段代码具有二次复杂度。 - Rudolf Adamkovič

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接