Allow Instance:GetDescendants(), :GetChildren() and :FindFirstChild() to take lambda functions as an arg

I want to write this code to flatten a tree of items I have to sell in my store:

items = game.ReplicatedStorage.ShopItems:GetDescendants(
    function(i) if i.className ~= "Folder" then return true end end
)

If you are writing a store gui, you want to take a database of items and filter by different characteristics. The dev experience is poor because Roblox Lua gives no support for associative filtering and sorting.

As a result developers either:

  1. Write very ugly unmaintainable code full of for loops and ifs
  2. Write their own collection classes for dealing with this stuff

Either way, a lot of wasted productivity. I want to make games, not collection classes.

I don’t know how to do substantially better than:

if mode == "shop" then
		local items = {}
		if filter == "All" then
			local temp = game.ReplicatedStorage.ShopItems:GetDescendants()
			for i=1,#temp do
				if (temp[i].ClassName ~= "Folder") then
					items[#items + 1] = temp[i]
				end
			end
		else
			items = game.ReplicatedStorage.ShopItems[filter]:GetChildren()
		end

Maybe I’m an idiot.

76 Likes

I like this a lot! It would also discourage more API-cluttering functions like FindFirstChildWhichIsA from cropping up, as we’ll have a general-case alternative for the following things we might want to consider:

  • CollectionService tags
    (Aside: We’re still waiting on instance-based tag methods, eg Instance:HasTag…)
  • Attributes
  • Instances that contain other kinds of objects, like Models with Humanoids
7 Likes

Yeah this seems like an API design anti-pattern.

What do you do the next time you want a different filter? Make a new function?

What if you want to find the first child that is an X but not a Y (subclass of X)? Do you write a for loop? Two for loops?

5 Likes

I like this idea a lot. Would love to see it implemented. It would make development a lot easier and save a lot of precious development time.

2 Likes

Definitely would love to be able to pass a filter lambda to control what results I get back from an object list get. I can’t count the number of times I’ve had to write boilerplate to refilter results (either continues in my for loops or recreating the table with only the instances I want) or the mistakes I’ve made while trying to do quick testing, iteration and automation with loops and object tables.

Would be cool to extend this to other gets like GetPlayers and GetTeams.

I’ve already got an entire suite of extension methods in my own personal library for predicate-based functions for instances and tables alike. They have been and always will be very useful for my development style.

I’m all for this change, as they introduce a lot of power into these queries.

3 Likes

This could also be implemented as a table library function for filtering a list. It would be an extra line of code, but could be helpful in a more general sense.

Sounds neat and I can imagine some uses for it, but in the case stated here:

In my opinion it just feels shorter because it’s all collapsed into one line? If given indention then it ends up being the same as a for loop.

image

image

If you compact the contents of the for loop a bit in the same manner as what you were asking for, you end up with this:
image

Unless I’m missing something here? I thought in this case maybe it was due to the performance implications of items[#items+1]=v or something but…

In this case however, I’d love something like this. It’d greatly reduce the need for so many separate versions of these…

image

1 Like