Deep WaitForChild and FindFirstChild functions

Chaining WaitForChild and FindFirstChild can generate lots of junk variables and redundant logic.

Consider:

local remoteFolderRbx = ReplicatedStorage:FindFirstChild('Remote')
local avatarEditingRemoteFolderRbx = if remoteFolderRbx then remoteFolderRbx:FindFirstChild('AvatarEditing') else nil
local resnapRemoteRbx = if avatarEditingRemoteFolderRbx then avatarEditingRemoteFolderRbx:FindFirstChild('Resnap') else nil

if resnapRemoteRbx then
	resnapRemoteRbx:FireClient(player)
end

The variables remoteFolderRbx and avatarEditingRemoteFolderRbx only exist so that I can introduce a branch that tells the operation whether to continue or not. It would be more concise and performant to use a single function that handles this all-or-nothing operation for me.

local resnapRemoteRbx = ReplicatedStorage:Find('Remote.AvatarEditing.Resnap')

Keeps the code cleaner and performs better.

10 Likes

If I’m not mistaken, you can literally use FirstFirstChild’s recursive boolean to iterate through all descendants and find it

link

. Also, why not just do local resnapRemoteRbx = ReplicatedStorage.Remote.AvatarEditing.Resnap ?

1 Like

Well going off you’ve got fireclient server doesn’t need to wait for things to load, plus you can do a recursive search with findfirstchild if you add true to the second parameter.

^ Using the second parameter for FindFirstChild is not always an option if there can be multiple descendants of the same name. OP’s function suggestion makes a lot more sense imo.

You can already do something similar. Here is my preferred version:

-- Usage:
getInstance(game.ReplicatedStorage, "Remote", "AvatarEditing", "Resnap")

-- If the target instance is not found, nil is returned as the first value, and the last found child as the second value
function getInstance(parent: Instance?, ...: string): (Instance | nil, Instance?)
	if parent then
		local nestedAmount = select("#", ...)

		local instance = parent

		for index = 1, nestedAmount do
			local name = select(index, ...)
			local currentChild = instance:FindFirstChild(name)

			if not currentChild then
				return nil, instance
			end

			instance = currentChild
		end

		return instance
	end

	return nil, nil
end

But having a built in function would be nice.

2 Likes

I do unfortunately see a potential issue with needing to escape instance-names which include dots within either their name or one of their ancestors’ names, though. As a bit of reference, the functions GetFullName and debug.info("s") are a bit of a nightmare to use because they don’t support any escape-characters and hence it can be ambiguous as to what instance they are referring to; just in-case the actual referenced instance actually has a dot anywhere in its name or one of its ancestors’ names. For example, with the following simplified hierarchy; if we were to search for Workspace.Model.Test.Part:

Workspace
├── Model
│   └── Test.Part
│   └── Test

It’s ambigious as to whether we are searching for “Test.Part” under “Model” which exists; or “Part” under “Test” which doesn’t exist and hence should return nil. Ideally, we’d like to have way of referencing both so we’d either need to have an escape character in-order to clear up any ambiguity without making certain instances inaccessible through the method; or alternatively a seperate way to reference the path which doesn’t see this ambiguity limitation.

1 Like

so… why does the 2nd argument of FindFirstChild not work for you in this scenario?

Why would you need to find a specific object that has the same name as multiple other objects and why would you have a findable object have the same names as other objects in the first place :confused:

isn’t Instance | nil the same exact thing as Instance??

Why would you need to find a specific object that has the same name as multiple other objects and why would you have a findable object have the same names as other objects in the first place :confused:

yeah, this was my question exactly. if you ever have a scenario like this, that isn’t the engines fault, that’s purely the user who is at fault.

the only ever time where you would need to have access to a specific instance that shares the same name with other instances under the same instance, would be if something interacts with said instance (lets say its named “button” under the “buttons” folder) and you need to change its parameters.

however, what you would do in this case is loop through each child and give it a mb1 click event connection (just an example if you are using a click detector) and run the logic there… this will give you access to the button instance (since you are looping through each one) so there shouldn’t be a problem

if each button does a different thing, then name the buttons after what they do!

2 Likes

Guys, I had a crazy idea. Just don’t add dots to actual object names. It’s time-consuming to get the object in scripts and just confusing in general.

2 Likes

Then you shouldn’t be using FindFirstChild and instead use loops to get all instances.

a loop only works if your goal is to get all instances with that name… which is not always your goal.

Thats what I had in my reply and thats basically what this suggestion is about

This reads as looking for engine compensation for poor organization. In the example given, you’re looking to reflect state behavior through instance existence and then citing the fact that checking in the way leads to bloated code. That is correct, it does. The better question is why are you reflecting your game’s states this way when there are lots of cleaner ways (e.g., modules in the most general sense or state management libraries) to go about it?

1 Like
local function deepFFC(parent,completeName)
	for _,name in string.split(completeName,".") do
		parent = parent:FindFirstChild(name)
		if not parent then return end
	end
	
	return parent
end

-- Example
local part = deepFFC(workspace,"Folder.Model.Part")