How do I make a loop only select an identical parent once?

Hi, I’ve been wondering, how do I make printing out a table unable to print identical values? Whereby there are multiple parts for a parent, and I want it to only print the parent once if found but still have different parent.Name printed

local parts = workspace:FindPartsInRegion3WithIgnoreList(region, ignore)

for i,v in pairs(parts) do
    print(v.Parent.Name)
end

I have never heard of this “FindPartsInRegoin3WithIgnoreList”. Could you please explain a bit more? Tbh, I am a bit confused. Where are the variables for region and ignore coming from?

Edit: I am looking at this on the DevHub now.

I found it over here:
https://developer.roblox.com/en-us/api-reference/function/Workspace/FindPartsInRegion3WithIgnoreList

and the variables and etc, are left out for the sake of simplicity.

local Centre = script.Parent.Part

local region = Region3.new(Centre.Position - Centre.Size/2, Centre.Position + Centre.Size/2) 
local ignore = {
	Centre, workspace.Baseplate
}
local parts = workspace:FindPartsInRegion3WithIgnoreList(region, ignore)
--- continue from the script above

Okay thank you! I will look into that link.

I would add a tag via CollectionService. You can tag the parent for every part, but the parent will only be tagged once. Then you print the results of GetTagged.

CollectionService:GetTagged (roblox.com)

1 Like

I’m not so familiar with CollectionService:GetTagged. May you show a way that I can fit inside my script? Also thank you!

Someone will probably get to it before me. I’ll be back in a couple hours and can flesh this out. Something like this. (and no, I’m not good enough to provide working code first try! LOL):

local CollectionService = game:GetService(“CollectionService”)
local parentTag = “PARENT”

– Loop through the parts.
CollectionService:AddTag(part.Parent, parentTag)

taggedParents = CollectionService:GetTagged(parentTag)

for v, parents in pairs (taggedParents) do
print(parents.Name)
–and if you need to remove the tags after you print them
– CollectionService:RemoveTag(parents, parentTag)
end

1 Like

cc @GolgiToad

Arguably a cleaner way (and more perfomant) would be to simply keep track of the parents already found and not bother with adding & removing CollectionService tags.

Additionally, if you accidentally use the same tag in two places, you will have a very nasty bug that will be hard to figure out due to the temporary nature of these tags.

Using parents are indices:
local parts = --Defined somewhere

-- Define a table of parents to check for duplicates
local parents = {}

-- Loop through each part
for _, part in ipairs(parts) do
    local parent = part.Parent
    
    -- Check that parent is not duplicate
    if not parents[parent] then
        -- Mark as duplicate
        parents[parent] = true
        -- Run code with parent, like:
        print(parent:GetFullName())
    end
end

-- Prevent memory leaks
parents = nil
Using table.find:
local parts = --Defined somewhere

-- Define a table of parents to check for duplicates
local parents = {}

-- Loop through each part
for _, part in ipairs(parts) do
    local parent = part.Parent
    
    -- Check that parent is not duplicate
    if not table.find(parents, parent) then
        -- Mark as duplicate
        table.insert(parents, parent)
        -- Run code with parent, like:
        print(parent:GetFullName())
    end
end

-- Prevent memory leaks
parents = nil

Both seem to perform better than using CollectionService, but compared to each other they perform equally. So, based on my testing so you can choose the table.find or indices approach, but I wouldn’t use CollectionService.

2 Likes

OK I’m back. First, I wholeheartedly agree one one point: don’t get your naming conventions twisted, tags or otherwise. Second, I make recommendations based on what works for me. I’ve been at Lua for a few months, so I’m by no means an expert. That said, CollectionService will not cause performance issues before my skinned mesh parts cause performance issues. I will break studio with animations.

The only issue I have run into with tags, and can gladly come back to for the OP, is that communication from CollectionService only really works from the server to the client. If you update a tag on the client, the server ignores it. That was a minor hurdle, otherwise tags are amazing.

I would not build a table that persists through game states. I find CollectionService is more versatile and it builds the tables for me.

I am sensing clues from the OP that leads me to recommend Tags over a table. I do not believe the greater purpose of this table is to fire-and-forget. Parts will be accumulated, either all at once (explosion), or over time (zombie apocalypse), and then something else important happens.

In an explosion, a table would handle the allocation of damage once to each player. Easy enough. But what if that explosion had a sustained effect, like acid or fire? I would then want to monitor the states of those objects. I would not include my acid damage code inside my explosion code, though I suppose I could. I would tag the players affected, dish out damage via sustained effect code, and remove the tag via death or item use.

Summary #1: I could use a table… but I’d program with tags, just in case I get ambitious.

In a zombie apocalypse, I would have countless interactions between infected and uninfected players and NPCs. That’s perfect for CollectionService. I don’t need to look up a table at all. Just grab “HasTag” each time 2 characters meet. I can’t imagine how a table would be more efficient when the number of infected grows. I have the characters, so I look at them.

Summary #2: Tags are labels, just read the label.

Personally, I use tags as a monitoring service, similar to the zombie apocalypse scenario. My npcs have multiple states that they will go through:

  1. Aggro
  2. Chasing
  3. Ranged combat
  4. Melee combat
  5. Lost aggro
  6. Return to spawn, or despawn
  7. Idle

It would make no sense to monitor hundreds/thousands of idle NPCs in relation to dozens/hundreds of players. So I tag which areas the players are in (by collecting parents of touching parts), and only check for aggro in those areas. I tag when aggro conditions are met, then only move those NPCs, check for combat, add/remove tags, and so on.

Summary #3: Tags can be used for efficiency too.

/shrug

1 Like

The table is immediately set to be garbage collected when set to nil. Meanwhile, using CollectionService has several events which are fired, or at least checked if not connected. Even if this argument were the case, CollectionService would be much less efficient anyways.

In this case, you would be right. However if tags are not needed yet, they should be added when needed, not just because they can.

Ideally, tags are only used when multiple scripts need to access a shared collection of parts, or when you need to hook up an event to a part being added or removed. Otherwise, there’s really no need.

At the end of the day, it really is preference. Use what you feel best fits the use case.

2 Likes