GUI "FindFirstChild" Function I created

Curious to see how efficient you guys think this code is, and if there are better ways to code it. I use this in my game to grab GUI instances with clean code.

function gui:Get(...)
	local guis = {...};
	local object = gui.Container;
	for _, guiObject in pairs(guis) do
		if (object:FindFirstChild(guiObject)) then
			object = object:FindFirstChild(guiObject);
		elseif (object:FindFirstChild(guiObject, true)) then
			object = object:FindFirstChild(guiObject, true);
		else
			while (not object:FindFirstChild(guiObject)) do wait() end
			object = object:FindFindFirstChild(guiObject);
		end
	end
	return object;
end

How it Works
This function was specifically created for grabbing any GUI instance within a parent instance. gui.Container refers to a frame that contains all GUI in my game (StarterGui.Main.Container) The function declares the variable object as the “root” instance to search through, then searches through all descendants of object until it finds it. Please note that this function expects the object to be there, or else there will be an infinite yield. The function also only returns the first occurrence of the instance that it is searching for.

Syntax
Since gui.Container (StarterGui.Main.Container) is the root object, any argument of gui:Get() is expected to be a descendant of gui.Container
Example:
(For grabbing StarterGui.Main.Container.Backpack.Items.Container.Banana):
gui:Get("Backpack", "Banana");

Probably should have tested this first, since ‘FindFindFirstChild’ would error :stuck_out_tongue:

Anyways, hard to tell why you’d want to use this instead of just the recursive FindFirstChild. The main difference, it seems, is that your function can tell you which ancestor to search in, i.e. Container.Backpack.Items.Banana and not Container.Backpack2.Items.Banana

But in that case I don’t see why you wouldn’t just use something like…

local backpack = container:FindFirstChild("Backpack", true)
local banana = backpack:FindFirstChild("Banana", true)

--Or with your function, something like...
function gui:Get(...)
	local guis = {...};
	local object = gui.Container;
	for _, guiObject in pairs(guis) do
		object = object:FindFirstChild(guiObject, true)
	end
	return object;
end

This way you wouldn’t get infinite yields for something that doesn’t exist (and for that matter, your function would yield infinitely even if ‘Banana’ was added to Items.Container later on, since it doesn’t search recursively for it).

2 Likes

Don’t use pairs() for this, it may work for now but it’s not guaranteed to iterate over the key-value pairs in any specific order. Using ipairs() would be an easy fix, but using a for loop would be fastest.

Also at a glance there are some redundant :FindFirstChild calls, but I doubt you’re running this enough to have any significant performance impact.

1 Like

???

I’m genuinely curious as to what prevents me from just using ::GetDescendants() and filtering the results instead. Maybe I’m misreading what this is intended for?

1 Like

Might be a good idea. GetDescendants is a relatively new function so I overlooked it. Thanks for letting me know. That’s the whole point I posted it :slight_smile:

Thanks for the heads up!

Instead of using a while-loop, you can use the DescendantAdded event to detect when a new descendant was added. Here’s my refactor of your code:

function gui:Get(...)
	local guiNames = {...}
	local object = gui.Container
	for _, guiObjectName in ipairs(guiNames) do
		local newObject = object:FindFirstChild(guiObjectName, true)
		while newObject == nil do
			object.DescendantAdded:Wait()
			newObject = object:FindFirstChild(guiObjectName, true)
		end
		object = newObject
	end

	return object
end
1 Like

Thanks for the improvement :slight_smile: