Search code examples
arrayssortingluaroblox

How to remove specific children from an array depending on name


I'm creating an obby and for one of my levels I plan on making a platform that when stepped on changes places based on where I have these "Checkpoints" set for the platform to teleport to. I have a folder in the Explorer called "Levels" and inside the folder are the levels and I've added all the parts required to build the level attached to a parent part. I've added all the children to the levels parent part into an array using the :GetChildren() function and now I'm trying to sort through and remove any part that isn't a checkpoint. All checkpoints are called Tele1, Tele2, Tele3, etc... Currently I'm using Table.remove but I've heard that it's bad and should never ever be used but I'm also kind of a newbie so I don't know how else to go about this. Here's my code:

local checkpoints = game.Workspace.Levels.Level3:GetChildren()

for i, v in pairs(checkpoints) do
    if string.match(v.Name, "Tele") then
        
    else
        table.remove(checkpoints, i)
    end

    print(i, v)
end

printing to see if the tables been correctly sorted.


Solution

  • First, as a side-note, I would recommend storing the folder of levels not in game.Workspace but in ServerScriptStorage. My example below is based on this.

    Instead of removing parts from the table produced by the call to the GetChildren(), you could more easily iterate through that table and grab all parts that you consider checkpoints—e.g.:

    -- The pattern that will be provided to string.match: it contains anchors
    -- to the start and end of a string, and searches for 'Tele' followed by
    -- one or more digits. This should be treated as a constant, hence the
    -- capital letters.
    local CHECKPOINT_PATTERN = "^Tele%d+$"
    
    local serverStorage = game:GetService("ServerStorage")
    local levels = serverStorage:FindFirstChild("Levels")
    local level3 = levels:FindFirstChild("Level3")
    
    -- Helper function to check if a particular part is a checkpoint.
    -- Requires: part is a BasePart.
    -- Effects : returns true if part.Name matches CHECKPOINT_PATTERN,
    --           false otherwise.
    local function is_checkpoint(part)
        return part.Name:match(CHECKPOINT_PATTERN) ~= nil
    end
    
    -- Get children of Level3, which should contain x number of checkpoints.
    local children = level3:GetChildren()
    
    -- Create a new table into which you will insert all parts you consider to
    -- be checkpoints.
    local checkpoints = {}
    
    -- Iterate through the table children to find checkpoints.
    for _, child in ipairs(children) do
        if (is_checkpoint(child)) then
            table.insert(checkpoints, child)
        end
    end
    
    -- To further abstract this process, the above for-loop could be placed
    -- inside a function that returns a table of checkpoints; but, this is
    -- entirely up to you and how you want to set up your code.
    
    -- Continue with code, now that all checkpoints within Level3 have been
    -- located and inserted into the table checkpoints.
    

    This is far simpler than modifying the original table produced by game.Workspace.Levels.Level3:GetChildren().

    As an alternative, more advanced solution, you could, depending on how exactly you have set up your place, use the CollectionService to tag all parts that you consider to be checkpoints; you could them retrieve a table containing these checkpoints by calling CollectionService:GetTagged(<tag>). See this page on the Roblox Wiki for reference.

    Edit for clarification:

    The variable—which I am treating as a constant—called CHECKPOINT_PATTERN is a string pattern; I use it in the same role as your string.match(v.Name, "Tele"), though my version is slightly different. To explain: the characters ^ and $ are called anchors—the former means that the pattern match should start at the front of a string, and the latter does the same but indicates the end; the "Tele" is the same as in your original code; and "%d+" matches a digit one or more times. See this page for more information on string patterns in Lua.

    As for the issue of putting your Levels folder in ServerStorage, this is a good idea as it better organizes assets in one location; and, it is more secure than storing things in, for instance, game.Lighting. I suggested this as a matter of good practice, but it of course depends on how your place is set up—if doing so removes your entire obby, then some restructuring of your place and code would be needed to make this work.