Pass type string as a Python Variable to be executed - Looking for best alternative

I am placing BuiltInCategory elements on company worksets. I have a list of worksets with the associated Built-In Categories as seen in a singular example below “a_Substructure”. I realize I could add the BuiltInCategory. to each OST_Category, but I thought I was more efficient by not adding it everywhere. I was unable to find an alternative to the solution below. I am learning and would like to know better alternatives to the below code as it feels like a hack/ workaround, not a solution. Any feedback is appreciated. Thank you.

Python eval() Evaluates and executes an expression of type string

import clr
clr.AddReference('RevitServices')
clr.AddReference('RevitAPI')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import*

doc = DocumentManager.Instance.CurrentDBDocument
constant = "BuiltInCategory."
a_Substructure = ["OST_EdgeSlab","OST_StructuralFoundation"]
elemsOfCategory = []

for i in a_Substructure:
    x = "%s%s" % (constant, i)
    y = "FilteredElementCollector(doc).OfCategory(" + x + ").WhereElementIsNotElementType().ToElements()"
    for j in eval(y):
	    elemsOfCategory.append(j)

OUT = elemsOfCategory
1 Like

Hello @dbrokaw
you can use the QuickFilter ElementMulticategoryFilter()
https://www.revitapidocs.com/2015/8d2774eb-3c47-5c3d-2866-8d4ab7408d2d.htm

import clr
import System
clr.AddReference('RevitServices')
clr.AddReference('RevitAPI')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

from Autodesk.Revit.DB import *
from System.Collections.Generic import List

a_Substructure = [BuiltInCategory.OST_EdgeSlab,BuiltInCategory.OST_StructuralFoundation]
typed_list = List[BuiltInCategory](a_Substructure)
filtercat = ElementMulticategoryFilter(typed_list)

elemsOfCategory = FilteredElementCollector(doc).WherePasses(filtercat).WhereElementIsNotElementType().ToElements()

OUT = elemsOfCategory
2 Likes

@c.poupin Ahhhh… ! I like it. Thank you for the feedback!
This is a good pattern to employ in the future, thank you for that. This community rocks!

2 Likes

typed_list = List[BuiltInCategory](a_Substructure)
I am looking to understand this syntax better using

but would love to have a helping hand in plain English if someone can break it down…
variable_name = List [Autodesk.Revit.DB.BuiltInCategory] (IEnumeratorOfTWrapper.Current)

I read it as follows:
variable_name = List [LookUpBuiltInCategory] (FilterFor&ReturnOnlyItemsMatching)
or
variable_name = List [MakeNewListOfBuiltInCategory] (ItemsToBeAdded)
I feel Like I got this wrong…?

the code with some comments

#create a Python list of BuiltInCategory
a_Substructure = [BuiltInCategory.OST_EdgeSlab,BuiltInCategory.OST_StructuralFoundation]
#Create a Net List (Generic Type) and cast the previous Python list 
typed_list = List[BuiltInCategory](a_Substructure)

## another way ##

#create a Python list of BuiltInCategory
a_Substructure = [BuiltInCategory.OST_EdgeSlab,BuiltInCategory.OST_StructuralFoundation]
#create a empty Net List (Generic Type)
typed_list = List[BuiltInCategory]()
#poupulate the Net List with Net Add() method
for i in a_Substructure:
	typed_list.Add(i)

some explanations here IronPython .NET Integration

2 Likes

@c.poupin Wow! Thanks!
The second description really drives it home for me. Again, thank you for your time!

This might be a bit more efficient at scale (in completely non-scientific study with a sample size of one was 2x faster), and has the added benefit of keeping stuff grouped by the category:

import clr #adds the common language runtime to the Iron Python environment
clr.AddReference('RevitServices') #adds the Revit Services class to the CLR
from RevitServices.Persistence import DocumentManager #adds the document manager tot he Iron Python Environment
doc = DocumentManager.Instance.CurrentDBDocument #sets the doc variable to the current Revit document
clr.AddReference('RevitAPI') #adds the Revit API class to the CLR
from Autodesk.Revit.DB import BuiltInCategory, FilteredElementCollector #imports the builtin category and filtered element collector to the Iron Python Environment
categories = [BuiltInCategory.OST_Roofs, BuiltInCategory.OST_Floors, BuiltInCategory.OST_StructuralFraming, BuiltInCategory.OST_Stairs, BuiltInCategory.OST_Walls ] # a list containing all the hard coded category names
collections = [ FilteredElementCollector(doc).OfCategory(cat).WhereElementIsNotElementType().ToElements() for cat in categories ] #uses list comprehension on the list of categories to created a fitlered element collector on the current document to pull elements (not types) as elements
OUT = collections #outputs the collections to the Dynamo environment
2 Likes

@jacob.small, THANK YOU!
I recently learned of List Comprehension and appreciate its simplicity. This is an excellent use of it and 2X is impressive. That will no doubt reduce the refactoring later.
Thanks again for taking the time!