How do I get the descendant of one thing that matches the name of another descendant of a different thing

I should’ve been a little more specific with my last post. How do I search through the children of children?
how do I get the descendants, and how do I compare them?
right now I have two similar things

image

image

this is for a theme system I am trying to write, kind of similar to the one arsenal uses. my issue is I need to figure out how to go through all the descendants of one and find the other’s counterpart, then apply the set values in the folders of one into the values of the UI. I thought about using GetDescendants() and FindFirstDescendant(), but unfortunately I get an error that is confusing, saying that it’s not enabled. please help

I’m not sure which parts you are trying to differentiate, but you can use v:IsA() to identify if the object iterated is the specified classname. For example, if you wanted test to see if a object iterated had the name of “Main” and was a folder, you could do:

for i,v in ipairs(object:GetDescendants()) do
    if v:IsA('Folder') and v.Name == 'Main' then
         -- do stuff here
    end 
end
1 Like

I meant to find the counterpart of one descending group of things. for example, if I get the canvas group folder, it should try to find the canvas group UI and then set the UI color to the color specified in the folder

Ah, I see what you mean. Instead of using GetDescendants(), do a nested for loop using GetChildren(). For example, do a for loop in the main directory and get the children of which I assume would have all your folders and GUI’s, etc. Then check if the name of object is “CanvasGroup” and if it is, do another for loop with GetChildren. If you have different types of UI instances in the folder, you can use :IsA().

for i,v in ipairs(maindir:GetChildren()) do
     if v.Name == "CanvasGroup" and v:IsA('Folder') then
          for t,k in ipairs(v:GetChildren()) do
                -- do stuff
          end
     end
end

There are likely better ways depending on exactly what you need done, but you could do something like this

local uiRoot = Customization --set this to the path of the ui "Customization"
local folderRoot = Customization --set this to the path of the folder "Customization"

local function myCode(ui, folder) --put whatever code you want in this function.  Those 2 variables will match.  You need to check that it's an item you want first
    --code here
    print(ui:GetFullName())
    print(folder:GetFullName())
    print("---------------------------------------") --This example just prints the names to show that they match in different locations.  (though I don't have studio installed atm, so I can't test, but I can't see a reason it wouldn't work
end

local function matchRecursive(ui, folder)
    if ui:GetChildren() and folder:GetChildren() then
        for _, item in pairs(ui:GetChildren()) do
            if folder:FindFirstChild(item.Name) then
                matchRecursive(item, folder[item.Name])
            end
        end
    end
    myCode(ui, folder)
end

matchRecursive(uiRoot, folderRoot)

The myCode function is set up to be called for every match. It’s like that function is connected to a getDescendants loop. So just run whatever code in there.

1 Like

I can’t seem to get it to work, and I am assuming that the “matchLoop” is a mistake meant to be matchRecursive since it is underlined in red and left as an undefined function, and the names seem similar enough, but even after changing that I can’t seem to get it to print anything more than the first UI and folder objects. also I noticed that it’s checking the UI for a name that matches the folders, but I am trying to check for UI that matches the name of the folders so I changed that too.

local uiRoot = script.RootUI --set this to the path of the ui "Customization"
local folderRoot = script.Theme.Value.UIOverrides.Customization --set this to the path of the folder "Customization"
local colors = folderRoot:WaitForChild("Colors", 5)
local UIOverrides = folderRoot:WaitForChild("UIOverrides", 5)

local function myCode(ui, folder) --put whatever code you want in this function.  Those 2 variables will match.  You need to check that it's an item you want first
	--code here
	print(ui:GetFullName())
	print(folder:GetFullName())
	print("---------------------------------------") --This example just prints the names to show that they match in different locations.  (though I don't have studio installed atm, so I can't test, but I can't see a reason it wouldn't work
	
	local StringVal = folder:FindFirstChildWhichIsA("StringValue")
	if folder:FindFirstChildWhichIsA("StringValue") then
		if StringVal.Name == "BackgroundColor3" then
			ui.BackgroundColor3 = colors:FindFirstChild(StringVal.Value).Value
		end
	end		
	
end

local function matchRecursive(ui, folder)
	if ui:GetChildren() and folder:GetChildren() then
		for _, item in pairs(ui:GetChildren()) do
			if folder:FindFirstChild(item.Name) then
				matchRecursive(item, folder[item.Name])
			end
		end
	end
	myCode(ui, folder)
end

matchRecursive(uiRoot, folderRoot)

I would do

local Names  = {}

for i,v in pairs (path:GetDescendants()) do
  if not table.find(Names,v.Name) then
    table.insert(Names,v.Name)
 else
 -- do code you want to do

end

Try adding an Attribute to both the UI element and the Folder element, then in a script look for UI/Folder that matches the value in the attribute.

Let’s say UI Tab1 has an Attribute “ID” with a value of 1
The folder Tab1 has the same attribute with a value of 1 too.

In a for loop iterating through the ui do.

if v:GetAttribute("ID") == "FolderTabHere" then
-- code
end

Of course you need to define the folder tab aswell.

I’d need to make a ton of different attributes then + I’d still need to figure out the script to “match” descendants

based on what I’m seeing, that checks and stores names, I don’t think that would try to match one group of descendants to another

that would be checking specifically for a folder named canvas group, then get it’s children. what I’m looking for is to “match” an item to another of the same name in a similar line of descendants.

Well from the two screenshots at the top of this post I don’t really know how your theme system is supposed to work.

I think it’s confusing, but my idea is to have a set list of colors in a theme.

then in each theme I set what thing has what individual color (I don’t set a value for it to pick a color in the color folder from in the UI itself because then it can allow me to have more control over the UI themes)

I think I have the solution to your problem.

Let me start off by saying that this is a really complex way of doing something really simple. You could keep each theme having different colors but use Attributes on your UI Elements to tell the script to apply specific properties to specific colors. This avoids the folder being extremely huge as you shown here.

But, I found a very quick method of doing this with a couple of things I made assumptions for

  • The values in each folder that represents UI isn’t a ‘Color3Value’, I seen that you were explaining that those are assigned to the 9 colors that each theme has, so I treat them as ‘StringValues’ instead.

  • A UI element can have the same name as another inside of the ancestor, so we can’t use GetDescendants because we can add a lot of confusion to the game engine and it might assign colors incorrectly when we try to get it’s counterpart.

I solve this by working recursively so I can pair an object in the theme’s UIOverride folder to an actual UI element.

This code should work just fine:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")

local themes = ReplicatedStorage.Themes
local theme = themes.Default --> This would be different by what theme they choose

local uiOverrides = theme.Overrides
local uiColors = theme.Colors

local function ApplyTheme(currentThemeFolder, currentGui)
	for _, child in pairs(currentThemeFolder:GetChildren()) do
		if child:IsA("Folder") then
			ApplyTheme(child, currentGui[child.Name])
		elseif child:IsA("ValueBase") then
			local colorName = child.Value
			local colorValue = uiColors[colorName].Value
			
			currentGui[child.Name] = colorValue
		end
	end
end

ApplyTheme(uiOverrides, playerGui)

I worked on fixing your problem in this place and I’ve attached it to this message if you want to see it in action.
ThemesExample.rbxl (38.5 KB)

2 Likes