FilteredElementCollector Basics

Let's master getting elements using FilteredElementCollector Class. It will be used in many of our future tools, so understand it well is crucial for any Revit API Developer.

FilteredElementCollector Basics

Let's master getting elements using FilteredElementCollector Class. It will be used in many of our future tools, so understand it well is crucial for any Revit API Developer.

Summary

What's FilteredElementCollector

Revit is like a Database with elements sorted by Classes, Properties and Parameters. And we can search for specific elements with FilteredElementCollector Class.

It has many methods that are simple to use to filter elements, but usually we will just need a few. We also could use Revit API Filters for more complex criterias, but we will talk about them in the next module.

FilteredElementCollector Anatomy

Let's have a look at how this class is being used.

You might get scared the first time you see that snippet, but it's actually very repetitive and we need to change very little to get another set of elements.

walls = FilteredElementCollector(doc)\
            .OfCategory(BuiltInCategory.OST_Walls)\
            .WhereElementIsNotElementType()\
            .ToElements()
floors = FilteredElementCollector(doc)\
            .OfCategory(BuiltInCategory.OST_Floors)\
            .WhereElementIsNotElementType()\
            .ToElements()

💡 Most of the time you will be using the same methods to get your elements, so once you use it a few times, you will realize that it's actually very simple to use.

Let's look at FilteredElementCollector Anatomy from PDF Guide I made.

We can break it down into 4 steps.

  • Create FilteredElementCollector instance by providing doc

  • Filter elements by Category or Class (or both)

  • Filter Types or Instances

  • Convert collector to Elements or ElementIds

👀 Let's look at these steps in more detail

Create FilteredElementCollector Instance

First of all we need to create an instance of that collector, so we can use its methods.

It's very simple, we just provide our doc variable, so it knows from which project it should get Elements.

collector = FilteredElementCollector(doc)

💡 There are 2 more constructors available, but we will explore them in the next lesson.

Once you have an instance of FEC, we can start applying methods to filter results. We can add them in any order we want and as many as we want to.

Let's look at the most common way of using it.

.OfCategory

Let's start by looking at .OfCategory Method.
This will get all elements matching provided BuiltInCategory.

This method takes single argument - BuiltInCategory.

It's an Enumeration, so it contains all possible values we can choose from.

We can also lookup the category name in RevitLookup .

Element -> Category -> BuiltInCategory

But most of the times, you can guess a name of a category, and pyCharm Autocomplete will help you choose the right one.

walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls)

💡 Be aware that Instances and Types can have the same category, so you will get both of them in the collector!

👀 Let's look at how to filter them apart.

.WhereElementIsNotElementType

To filter Instances or Types we need to use one of the following methods:

They don't take any arguments so we just need to call them on the collector. Since we are getting instances, we will use WhereElementIsNotElementType().

walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls)\
                                     .WhereElementIsNotElementType()

💡 It's still a single line! In python we can use '\' symbol to break line into multiple.

ToElements / ToElementIds

The next step we usually convert this collector into a list of Element or ElementIds.

💡It's worth noting that while you keep adding methods, it still returns you FEC type, but once we convert it into a list of elements, we can't use any FEC methods anymore.

We can also create a for loop using the collector, but I prefer to do it with a list instead.
To do that we will use one of the following methods:

These methods also have no arguments, so we just call any of them.

walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls)\
                                     .WhereElementIsNotElementType()\
                                     .ToElements()

💡 Once you turned collector into a list, you can't use any more FEC methods!

✅ And now we have a list of all Wall instances in our project!

.OfClass

As I mentioned, we could also use .OfClass instead of .OfCategory.

This method takes type as argument, which means any Class from Revit API.

We can see Classes in RevitLookup on the left sidebar above selected elements.

So let's get the same Wall instances using .OfClass

walls = FilteredElementCollector(doc).OfClass(Wall).ToElements()

It's also worth noting that Wall Instances and Types have 2 different classes.
One has Wall class, and the other uses WallType Class.

💡So we don't even need to filter Instance and Types. However, you still can add these methods if you unsure, it won't harm your code!

Category vs Class in Revit API

You might be wondering what's the difference between these 2 methods?

Well, as you might noticed elements can have the same category but different classes.
For Example Check out these screenshots of Wall and WallType in Revit Lookup.

This also includes In-Place Elements, because they have a class of FamilyInstance and any category that you assigned them.

So when we get our Walls with OfCategory, it will also include In-Place Walls. So if you would want to filter them out, just use OfClass and it will only get native Revit Walls.

💡In the beginning explore your elements with Revit Lookup and also make print statements of elements that you get to better understand what you are getting.

For Example here you can see that I have Elements selected of class Wall but they have a category of Curtain Panels.

Get Floors

As I mentioned, it's very repetitive when we work with FEC.

If I wanted to get all Floors or FloorTypes I would just need to change BuiltInCategory or Class depending on the method I choose.

floors           = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors)\
                .WhereElementIsNotElementType().ToElements()
floor_types  = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors)\
                     .WhereElementIsElementType().ToElementIds()

floors_          = FilteredElementCollector(doc).OfClass(Floor).ToElements()
floor_types_ = FilteredElementCollector(doc).OfClass(FloorType).ToElements()

💡 You don't need all 4 lines! Usually you need just 1, but I want to show you examples.

Get Rooms

Let's try to get all Rooms now.

If we would try to get Rooms with OfClass we would get an error saying that 'class exists in the API, but not in Revit's native object model'.

That's very rare, I only know Rooms with that behavior. In this case just use OfCategory to get them.

from Autodesk.Revit.DB.Architecture import *

#❌ rooms = FilteredElementCollector(doc).OfClass(Room).ToElements()
rooms = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().ToElements()
# print(rooms)
Import Classes from other Disciplines

When you work with Classes from different disciplines, you might need to import them from appropriate module. Not all Classes are stored in Autodesk.Revit.DB !

# Some classes are located in more specific namespaces!)
from Autodesk.Revit.DB.Architecture import *
from Autodesk.Revit.DB.Structure    import *
from Autodesk.Revit.DB.Plumbing     import *
from Autodesk.Revit.DB.Electrical   import *
from Autodesk.Revit.DB.Mechanical   import *

💡 If you want to find out where does the class come from, have a look in the Revit API Docs, you will see module where it comes from on the top about Class Name.

Get Loadable Families (Doors, Windows…)

Now let's get some Loadable Families like Door, Windows, GenericModel or other.
If you check in Revit API you will notice that there are no such classes available.

💡 That's because Loadable Families and In-Place Elements use a class FamilyInstance.

So Again, just use OfCategory to get these elements.

doors    = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Doors).WhereElementIsNotElementType().ToElements()
windows  = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Windows).WhereElementIsNotElementType().ToElements()
g_models = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_GenericModel).WhereElementIsNotElementType().ToElements()

Filter out In-Place Elements

If you are using OfCategory method to get these elements, you will also get In-Place Elements with the same Category.

Often times we would want to filter them out, and in this case we would need to look for property that reports that.

This property is available in:
Element -> Type -> Family -> .IsInPlace

We can create a List Comprehension to filter it

# FIlter Model-In Place Elements (MIP)
doors_no_mip= [door for door in doors if not door.Symbol.Family.IsInPlace]

💡 Symbol property returns FamilySymbol, which is a class of Types for Loadable Families!

Get Views

Lastly, Let's have a look at getting Views!
It's not as complicated, but it might get tricky if you compare OfClass and OfCategory.

# OfCategory
views  = FilteredElementCollector(doc)\
           .OfCategory(BuiltInCategory.OST_Views)\
           .WhereElementIsNotElementType()\
           .ToElements()
           
# OfClass           
views_ = FilteredElementCollector(doc).OfClass(View).ToElements()

print('OfCategory: {} Elements'.format(len(views)))
print('OfClass:    {} Elements'.format(len(views_)))

When I ran it in my project I got 406 vs 510 Views. That's a massive difference, so let's try to understand what's going on here.

Let's start by exploring View Classes.

First of all different views in Revit have different Classes, we can learn more about them by looking at Inheritance Hierarchy in View Class:

You can see all related classes in here from Top to Down.

View Class is used for all classes to provide main functionality. Therefore, we get all of them at once if we use OfClass(View)

But then each ViewType will have its own Class with additional Properties and Methods.

💡 We can also get specific views by using their individual classes like View3D

Views Overview

Let's write a code snippet to print all views that we get with OfClass(View) method, because we get more views there.

To make it easier to explain I will get Type, ViewType, and BuiltInCategory of each View and then print it as a table.

Thanks to pyRevit it's very easy to do. If you are going to look in Effective Outputs page of pyRevit Dev Doc you will find a section about Printing Tables.

We mainly need to prepare a list where nested lists will represent a row, and each item in nested list will represent a value in each column.

And here is the snippet that we can run

views_ = FilteredElementCollector(doc).OfClass(View).ToElements()

data = []
for view in views_:
    typ       = str(type(view)).replace('<type','')
    view_type = str(view.ViewType)
    cat       = str(view.Category.BuiltInCategory) if view.Category else 'N/A'
    data.append([typ, view_type, cat])

from pyrevit import script
output = script.get_output()

output.print_table(
    table_data = data,
    title = 'Views Overview',
    columns=['Type', 'ViewType', 'Built-In Category']
)

👇And it will return you a table looking like this.

You will notice that there are a lot of different Types, ViewTypes and Built-In Category. We have so many because we used OfClass(View), and all these views are using class that is based on that parent class, therefore we get them as well.

Learn more about inheritance in python if that doesn't make sense.

Best Way to Get Views

When I need to get any views, I prefer to get all Views by using OfCategory, because it will exclude schedules.

And then we can create list comprehensions to filter views by their ViewType property.

ViewType is an enumeration so we can pick one of possible values such as FloorPlan, ThreeD, Elevation, Section … Check out all members in Revit API Docs.

all_views  = FilteredElementCollector(doc)\
              .OfCategory(BuiltInCategory.OST_Views)\
              .WhereElementIsNotElementType()\
              .ToElements()

views_3D = FilteredElementCollector(doc).OfClass(View3D).ToElements()

views_3D_= [view for view in all_views if view.ViewType == ViewType.ThreeD]
views_el_= [view for view in all_views if view.ViewType == ViewType.Elevation]
views_sc_= [view for view in all_views if view.ViewType == ViewType.Section]

print('List Comprehension: {} Elements'.format(len(views_3D_)))
print('OfClass: {} Elements'.format(len(views_3D)))

all_view_templates = [view for view in all_views if view.IsTemplate]
no_view_templates  = [view for view in all_views if not view.IsTemplate]
ViewTemplates

By the way, ViewTemplates also use the same classes as regular views. But they have a property to check if it's a ViewTemplate or a regular view. call IsTemplate

You can filter them using the following snippet.

all_views  = FilteredElementCollector(doc)\
              .OfCategory(BuiltInCategory.OST_Views)\
              .WhereElementIsNotElementType()\
              .ToElements()
            
all_view_templates = [view for view in all_views if view.IsTemplate]
no_view_templates  = [view for view in all_views if not view.IsTemplate]

HomeWork

First of all Download and print out the PDF Guide provided in the Download Section. It has plenty of examples and additional explanation.

It also has a section about Revit API Filters, but you can leave it for the next module.

Then you task is to get different elements. Scroll through this post, or PDF-Guide and try to get different elements and print them in the console. Get creative and try other elements as well.

It might look like a complicated Class to work with, but by now you probably have 50+ examples on getting different elements. Just use different examples a few times and it will all make sense eventually!

Also Share your insights in the Discord Community. Let's see how well you understood the basics of FilteredElementCollector

✅Download PDF-Guide
✅Print PDF-Guide in A3/A4 Format
✅Practice Getting Elements with FEC
✅Share your Insights in the Community

⌨️ Happy Coding!

Questions:

Where can I get the PDF Guide?

Where can I get the PDF Guide?

Should I use OfClass or OfCategory?

Should I use OfClass or OfCategory?

Is there a simpler approach to get Elements?

Is there a simpler approach to get Elements?

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.