GetChildrenOfClass()

As a developer I find myself doing operations like this quite frequently:

local Children = Parent:GetChildren()

for i,v in (Children) do
 if v:IsA(‘ClassName’) then
  — operate
 end
end

In addition to constantly class checking, I feel guilty using :GetChildren() at all due to how expensive it can be.

A more efficient :GetChildrenOfClass() method would be very clean, useful, and would pretty much replace all of my uses of :GetChildren().

36 Likes

I don’t think it will be any cleaner since it will do the same thing as GetChildren() but also check for classes. If you want to add such a feature in a clean way, then you will probably have to rewrite some pieces of instance source code, which can lead to fatal errors in old places.

make a module script containing ur getchildrenofclass function.

7 Likes

There would probably need to be a GetChildrenWhichIsA (Are?) as well, since OfClass methods only looks for that exact class while WhichIsA respects inheritance.

GetDescendants variants would also need to be added, but I’m not too sure how pretty GetDescendantsWhichIs looks.

1 Like

The reason I want it to be it’s own operation is so it’s run faster and more efficiently than GetChildren()

GetChildrenWhichIsA would also be nice, but as you mentioned it doesn’t look as nice :slight_smile:

1 Like

A module script wouldn’t work for his use case. He wants these methods to “avoid” the “expensiveness” of :GetChildren.

A module script would just be a “wrapper” of :GetChildren

1 Like

Way easier in what way? You’ll have to require the module in each script you want to use the method in, and GetChildrenOfClass(parent, classname) just looks weird when all other methods are integrated into the Instance class itself.

2 Likes

THANK YOU. I was literally thinking this exact thing yesterday when doing the stupid “for loop” solution you described. Glad someone else agrees

2 Likes

The issue is not that OP cannot make their own helper function; it’s that a native solution would be more efficient but currently does not exist.

3 Likes

We really need some kind of XPath/XQuery/other API for advanced lookups.

21 Likes
local module = { }

function module.GetChildrenOfClass(parent: Instance, className: string, func: (index: number, value: Instance) -> ())
    for index, value in parent:GetChildren() do
        if value:IsA(className) then
            func(index, value)
        end
    end
end

return module
module.GetChildrenOfClass(script.Parent, "Script", function(index, value)
    print(value.Name) -- "Script"
end)

To reiterate, I don’t want to just code my own because I’m asking for a more efficient built-in version.

1 Like

I really don’t see a use case for this outside of that it would spare you the 20 seconds of writing a conditional :GetChildren() function. I do wonder what it is you’re doing that has you worried about the “expensive” performance impact of :GetChildren().

GetChildren() is just expensive… that’s like, a well known fact. Regardless of use-case, we should all be striving for more efficiency.

Most of the time I’m doing class checking on what’s returned by GetChildren(). It’s a very obvious area for improvement. So it’d be nice if there was a method I could call that would be faster than anything I could code on the developer level.

Also, because it’s returning a smaller array than GetChildren() I would hope that the method itself would run faster as well.

I would say a common case would be querying all the descendant BaseParts for param objects and methods (i.e., RaycastParams.FilterDescendantsInstances & workspace/BulkMoveTo).

This normally is achieved with the following:

local Children: {Instance} = Container:GetChildren() -- 1000 Instances?
local Parts: {BasePart} = {}

for _, Child: Instance in Children do
	if Child:IsA("BasePart") then
		table.insert(Parts, Child)
	end
end

But this is bad because it requires Parts to be resized every time a new index is added and considering that Children could contain thousands of Instances, performance is going to suffer. Yes, we could pre-allocate Parts with table.create(#Children) but that allocation won’t downsize when only a few elements are added, so that’s off the table.

GetChildrenOfClass/WhichIsA gives a predictable and sound implementation of producing a single table that doesn’t require repetitive allocation in user code. I would argue this feature would be less for ergonomic use cases and more of a performant alternative to expensive code like shown above.

2 Likes

I would like if there was a filter callback parameter in the GetChildren method that looks something like this:

Lets say I wanted to get all decals in a Part

local Decals = Part:GetChildren(function(Instance: Instance)
    return Instance:IsA("Decal") -- boolean
end)

I don’t believe this would break any games, because why would someone pass a callback as the first argument.

I would say that this is better than adding a bunch of small APIs like GetChildrenOfClass, because people would probably want more of them. Instead just add a filter callback and it allows for anything really.

And this allows for a more “advanced” lookup system. This should also be added to GetDescendants, since they’re basically the same.

3 Likes

there should also be getdescendantsofclass

Gonna bump this. The lack of a GetChildrenOfClass is still a pain point for myself and other devs, and there’s precedent for something similar in the form of FindFirstChildWhichIsA / FindFirstChildOfClass.

Having a form of GetChildren which only scopes out specific instance types would not only improve readability and cleanliness of code. it’d also possibly be more efficient. GetDescendantsOfClass would also be appreciated!

Probably would not just be more efficient but more readable as well. Caching children would be much more easy.

1 Like