I don’t even know where to begin with this without making a tree for the hierarchy, which seems like a bad solution. Any help or pointers would be appreciated.
I haven’t really tested this but you can do something like this
local path={}
local function getpath(ins)
if not path[ins]then
path[ins]={}
local cur=ins
for i=1,1/0 do
path[ins][i]=cur
path[ins][cur]=i
cur=cur.Parent
if not cur then break end
end
end
return path[ins]
end
local function lt(a,b)
local ap=getpath(a)
local bp=getpath(b)
if a==b then return false end
if ap[b]then return false end
if bp[a]then return true end
for i=1,#ap do
if bp[ap[i]]then
local acur=ap[i-1]
local bcur=bp[bp[ap[i]]-1]
return acur.Name<bcur.Name
end
end
local acur=ap[#ap].Name
local bcur=bp[#bp].Name
return acur.Name<bcur.Name
end
local t=workspace:GetChildren()
table.sort(t,lt)
for k,v in next,t do
print(k,v:GetFullName())
end
That works remarkably, but I’m a bit worried about performance. This visibly seized Studio when I ran it over a relatively simple game as a test (the original Happy Home in Robloxia), which has 2911 instances as descendants of Workspace. The upper limit of what I’m anticipating using this for is around 4000, which means I’ll have to do some optimizations or modifications.
It’s a very good base though, so thank you! I’m interested if other people have solutions, so I’ll leave this thread unsolved for now, but if nobody comes up with anything better I’ll be sure to mark yours.
local t = {
workspace.Baseplate,
workspace,
workspace.Camera,
workspace.SpawnLocation.Decal,
game:GetService('Players'),
workspace.SpawnLocation,
game:GetService('Lighting'),
}
table.sort(t, function(a,b)
return a:GetFullName() < b:GetFullName()
end)
for i, v in pairs(t) do
print(i, v:GetFullName())
end
I think OP wants to have things that are deeply nested to be ordered last (otherwise they wouldn’t have mentioned hierarchy). e.g workspace.A.B.C.D would come after workspace.Z. Just sorting their full name doesn’t account for this. It’s possible that OP just chose a poor example.
EDIT:
workspace:
Results using Acreol’s script:
Results using your script:
The two scripts produce different results, so one (or both?) of the scripts must be wrong…
I can make it faster if you can tell me some information about the input (and your game hierarchy):
Do parts that need to be sorted change parents often?
Are they created/destroyed often?
What is the median depth of the parts in the game hierarchy? What is the max, and how often does it occur?
How can you provide the input? An array of parts to sort? An (array of) instance(s) whose descendants you want to sort?
Without loss of generality, the initial solution I said could be made faster if you microoptimize and binary search for their closest ancestor (but the binary search would only speed it up if you have really complicated hierarchies)
Edit: Also what will you be doing with the output? Do you need the entire hierarchy sorted? If its the first 100 or something we could partially sort instead
While I don’t think GetFullName should entirely be relied on, it seems the most performant way to do this. With roughly 2800 parts randomly inserted and parented into others;
local function parentDepth(instance)
local depth = -1
instance:GetFullName():gsub("%.",function(a) depth = depth + 1 end)
return depth
end
local function compare(a,b)
local depth1,depth2 = parentDepth(a),parentDepth(b)
if depth1 == depth2 then
return a:GetFullName() < b:GetFullName()
else
return depth1 < depth2
end
end
local t = workspace:GetDescendants()
local start = tick()
table.sort( t, compare )
print("Time taken",tick() - start)
print("For",#t,"instances")
for i = 1,#t do
print(t[i]:GetFullName())
end
If this is designed for making a tool like an explorer, this can be simplified by only actually sorting the GUIs and doing so when the parent changes. Given that there are list constraints, all that would be required is to change the layer order value and resize any containers. If this is designed for some other purpose, I think there would be ways to just parse them in order rather than sorting them which way be more performant than sorting into a list and then parsing. Doing something such as running a script for each child and storing their children in another array would allow you to execute things in order as you would go down the array by hitting the descendants in order, putting them after any siblings from the initial loop(s).
local printAll = function(parent)
local children,descendants,immediate
descendants = {}
children = parent:GetChildren()
for i = 1, #children do
--print(children[i]:GetFullName()) --commented for benchmark
immediate = children[i]:GetChildren()
if #immediate > 0 then descendants[#descendants + 1] = immediate end
end
while #descendants > 0 do
children = descendants
descendants = {}
for spot = 1, #children do
for i = 1, #children[spot] do
--print(children[spot][i]:GetFullName()) --commented for benchmark
immediate = children[spot][i]:GetChildren()
if #immediate > 0 then descendants[#descendants + 1] = immediate end
end
end
end
end
This won’t really be alphabetical though as GetChildren outputs based on the order of parenting.
However, this alone without all the printing runs pretty performantly, giving ~0.008999 seconds to go over the entire contents of the workspace in the City template (10696 instances).
Unfortunately GetFullName doesn’t escape the names of Instances, so it’s not super usable in this case.
The same problem arises with @TaaRt’s solution, so unfortunately I can’t use either of them (this is for something that people other than me will be using, so I can’t just not put a fullstop in Instance names).
This is indeed for an explorer-style GUI. I’m actually planning to use this sorting to determine the LayoutOrder of the various lines. The other option is manually traversing the game hierarchy and generating the LayoutOrders from there, which might be a better options. I couldn’t compare the two though because I didn’t know how to manage this method.
Layout orders shouldnt be necessary as i think it can be set to sort by naming. Simply just iterate through children and descendants and place a ui list layout inside of each group