Grouping, Sorting, and Filtering

New Components for Grouping, Sorting, and Filtering Objects

Edit: Some changes have been made to the way the Filtering components behave. To see the latest features, please review this post after reading through the tutorial below.

Have you ever wanted more granular control over the data or objects inside of Grasshopper? For example, have you ever wanted to select only the objects in your model that have a particular material applied to them (ie. glass)? Or maybe you wanted to remove all surfaces in your model that were on a specific layer and whose area was smaller than a certain value. Could you do it (easily)?

Grasshopper 1 for Rhino 8 WIP now has new components which will help you manage your data more effectively.

To explain how these new components work, it is best to start with a simple example. In this massing model there are BREPs representing the outside envelope of the buildings and surfaces which serve as the floors. The objects have been placed on different layers and have been assigned different colors based on their use type (ie. office, residential, etc.).

Most of the objects also have some meta data attached to them as user text entries.

Let’s begin this tutorial by figuring out the overall area of each building in our massing model. Generally speaking, there are three building masses in our model. Each of the floor plates have a Building ID number assigned to them as a user text entry.

Grouping By Attribute

To find the overall square footage of the floors, we need to first reference all of the floors in our Rhino model into our Grasshopper definition. This tutorial is all about filtering data - since we organized our model by layers, we can filter for the floors by using the Layer input of the Query Model Objects component. By using the “*” wildcard after the word Floors, it will select all the objects on the Floors sub-layers.

Next, we need to group each of the floor plates using the Building ID as our grouping criteria. Let’s use a Group By Attribute component and connect the output of the Query Model Objects component to the Content input.

This component uses the Key input to determine how to group content into different data tree branches. You can right-click the Group By Attribute component and choose whether you want the Key to be an attribute of the input data type, such as a layer name, a type of material, or a display color for example, or if you’d rather use a user text value.

In this case we want to group the objects based on the Building ID user text value, so we need to right-click on the component and select “By User Text Key”. Now when we right-click on the Key input we can select one of the available user text keys as our grouping criteria. In this case, we can select the Building ID node in the tree view.

Looking at the Content output, all of the floors have now been separated into different branches according to the Building ID number. The Values output returns a list of all the unique key values that it found in the content. In this case we see a list of three values (3, 1, and 2). You’ll notice that the values are not sorted. To sort the values, we’ll need to add a second component to our definition - Order By Attribute.

Ordering By Attribute

The Order By Attribute component is very similar to the Group By Attribute component in that it uses a Key to do something to (ie. sorting) the incoming list of content. And just like the Group By Attribute component, you can right-click on the component to determine if you want the Key to be a data type attribute or a user text key.

To sort by Building ID we need to right click on the Order By Content component and select “By User Text Key” and then select the Building ID node in the Key tree view. Now the content will be sorted, and we can then group the floors onto different branches using the Group By Attribute component.

The last step in this process is to add up all the area on each branch of our data tree. Since all of our floor surfaces are now separated into different branches, it’s quite easy to pass the output of the Group By Attribute component into an Area component and then add all of those values together using a Mass Addition component.

From the Grasshopper definition above, we can determine that:

  • Building 1 = 43,622 sf
  • Building 2 = 36,626 sf
  • Building 3 = 5,736 sf

Filtering Content By Rule

In addition to Grouping and Sorting, we also can filter content based on whether a certain condition is met (ie. rule). Typically, a rule will compare one object to another and return a Boolean value (ie. True or False). Think, is x equal to y? or is x greater than or equal to y? We can also combine those rules into more complex expressions using operators.

So, let’s take our existing model and create a rule which will filter out only the office floor surfaces whose area is larger than 2,250sf but smaller than 2,500sf.

Before we begin, let’s add a new user text entry which will contain a formula to calculate the area of each floor plate. The formula is dynamic and will update whenever the floor plate object is changed. To create the proper formula, we’ll use the Text Fields Expression component.

The default formula for this component is set to Area, but you can right-click on it to have it generate different types of expressions. Don’t forget to right-click on the Units input and set the value to Feet. Finally, let’s add a Bake Content component at the end of our definition to add the user text entry to each of the objects.


Let’s start by adding a Filter By Rule component to the canvas. Just like in the Group By Attribute and Order By Attribute, we have the option to use a data type attribute or a user text key as our filtering value. We know that all the floor plates have a user text entry called “Use Type” and that some of them (the red ones) have a value of “Office”.

So, let’s start by right-clicking on the Filter By Rule component and selecting “By User Text Key”. Now, right-click on the Key input and select the Use Type node from the tree view.

Next, we need to define a rule that compares the Key we just defined (ie. Use Type) and compares each value to the word “Office”. If those values are equal, then the object will be included in our list. So, let’s create an Equality rule and set the input value to “Office”.

The result of our first filtering rule is a set of 10 surfaces each of which describe a floor plate in the Office tower.


Next, we need to further reduce this list by filtering objects that are larger than 2,250sf and smaller than 2,500sf. To do this, we need to create two separate rules (x > 2,250) and (x < 2,500) which we can combine using the AND operator.

We also need to add a second Filter By Rule component and set it’s key to use the Area value we created a few steps above.

In the end, the result of our compound expression leaves us with 5 office floor plates, each of which are larger than 2,250sf and less than 2,500sf.



There is a lot that can be done with the techniques presented above, yet it’s difficult to describe all of the features in this format. For additional information, please check out this post on the topic.

12 Likes

It appears that when I input a value inside the component itself, it is treated differently than when I feed the same input in through a panel.

If I input a 0 in the Greater than rule component, all curves are passed on.

If I input a 0 in a panel, none of the curves are passed on, as I would expect given the numbers values are larger than 1.

As a feature request, I would like to see an inverse group option, which, based on an attribute key, outputs the objects that do not have that particular attribute.

Also interesting would be a “Does not contain” rule, to filter out objects that don’t have a certain attribute value assigned.

Hi @Intuos ,

I believe you can achieve that functionality with the “Equality Rule” component. Please see example below and let me know if this is what you meant or if I misunderstood?

Graph Space:

Model Space:

20230821_Group_Filter_Content_Exclude_Response_01a.gh (6.7 KB)

@michaelvollrath

Thing is, the Equality rule only filters for exact hits. Say, you set the Equality rule’s input to “Exam”, you won’t find any data. Neither if you set it to “Exam*” (note the asterisk at the end). So what I am asking for is something resembling the Text Match component, so you can search for bits and pieces of text values within User text values.

I see, yea it doesn’t seem to work:

I would expect the “Contains” component already should handle this but it doesn’t seem to be working properly?

You can see the text match component (as you mentioned) returns the results but yes it should be contained within the single “Contains Rule” node.

Model Space:

Interesting…

So, the “Contains Rule” is simply calling the String.Contains method to determine if there is a match or not. The “Match Text” component follows more of a regex matching algorithm to determine if a text is a match or not… so it’s not completely surprising that the two are giving different results. However, I can see adding a new rule for regex patterns (which has been requested before) as well as an output for “Does Not Contain” in the Contains Rule component. Would that help?

3 Likes

I’m curious on @Intuos thoughts but, yes, in but in my perspective yes that would be helpful! Thanks for breaking it down!

Regex rule wood be a great addition!

FYI. In the next WIP release, the Contains Rule component will now have a “Not Contains” output which is essentially the inverse of the the “Contains” output. @Intuos The substring matching should work better now as well. You’ll see in the example below that the value it’s looking at is MyRedCircle and I’m just using the word Red in my Contains rule… and if this substring is found in the user text value, then the result will be passed on.

Also note that I have inverted the logic of the “Case” input to match the equivalent input in the Match Text component. Essentially, before today the input was set to “Ignore Case”… which if true it meant that it would not take into account the case of the text when matching. Now, if the input is set to true then it will use case-sensitive matching in the comparer.

2 Likes

Sounds good, look forward to check this out. Thanks for adding and getting back to me @AndyPayne.

Would be cool if the same logic (the rules) could be aplied in sesrching for User text keys as well. If I need to know (filter) wheter an attribute is present or not and to then group the content still takes quite a bit of logic. And at the moment, baking particular values that have been recomputed in Grasshopper, proves to be more tricky then I expected.

The does not contain output works great, thanks! Using it already :smile::

I was wondering, though, why doesn’t the Filter Content component output the list of values as well, like the group content component? I think that in most usecases people would want to use the values they filtered for anyways. At least, I do.

For instance in the example above, I filter out the default attribute value, so I can work with those geometries that I modified the values of.

Great. Glad it’s working. There “may” be another addition to the rules in the next release for RegEx/Wildcard matching. But, that’s a different topic.
I think it was just oversight for why the value wasn’t included in the output like the Group components. I guess I thought that if you were filtering for a specific value, that you’d already know what that value is… but I can see now why adding the value as an output would be helpful. I’ll create a YT for this.

2 Likes

As for the conditional rules, could the And and Or rule get these dynamic inputs as well?

The And and Or gates have them as well. It would certainly reduce wiring and chaining Rules if you have more complex rules set up (like filtering for more than 2 does not contain inputs). Concatenate Text is on the right to illustrate the point.

Ok. I have made a YT issue for this request.

2 Likes

RH-76708 is fixed in the latest WIP

1 Like

@brian The YT link is not public. What was fixed exactly?

E1: I see that the Filter Content now has a value output, thanks!
Also the Logical And Rule component was updated (, but it doesn’t yet have the + handles for more inputs) as was the Contains Rule.
E2: Note that the components have to be updated manually as the Solution > Upgrade components button is greyed out.

The YT says it has public permissions set… But yes it was to show you that the Values output was added. In addition, we added a Logical Not operator to negate any filter rule condition. I have not yet added the multiple filter rules for the AND, OR, or NOT filters… but I plan to get to that soon.

Weird, I just updated permissions - can you see it now?

Yes it is fine now!