Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Family Instance Filter

13 REPLIES 13
SOLVED
Reply
Message 1 of 14
Dale.Bartlett
14546 Views, 13 Replies

Family Instance Filter

Honestly, sometimes I feel so daft; is it me or the API? I want to return a list of all Family Instances by name. All samples, including the Revit API docs, pass a name, but this is in fact the Type name. As the Type name is not unique (i.e. "Type 1") and can be used by different Families, this seems to be incorrect. The following is the example from the Revit API Docs:

 

// Use ElementClassFilter to find family instances whose name is 60" x 30" Student 
ElementClassFilter filter = new ElementClassFilter(typeof(FamilyInstance));
// Apply the filter to the elements in the active document
FilteredElementCollector collector = new FilteredElementCollector(document);
collector.WherePasses(filter);
// Use Linq query to find family instances whose name is 60" x 30" Student
var query = from element in collector
where element.Name == "60\" x 30\" Student"
select element;
// Cast found elements to family instances, 
// this cast to FamilyInstance is safe because ElementClassFilter for FamilyInstance was used
List<FamilyInstance> familyInstances = query.Cast<FamilyInstance>().ToList<FamilyInstance>();

 

To correctly return a list of FamilyInstances, surely the collector needs to be passed the following, in order:
1. Category
2. Family Name
3. Family Type Name

 

I have been unable to find an example, and I have been unable to assemble a filter statement to correctly limit the returned instances. Am I missing/overlooking something? Can anyone point me in the right direction? Thanks, Dale




______________
Yes, I'm Satoshi.
13 REPLIES 13
Message 2 of 14

Dear Dale,

 

Thank you for your query.

 

Definitely not daft; however, this information is out there in numerous previous threads and dozens if not hundreds of blog posts.

 

You do not need to apply specific filters, ever, or apply them in any specific order.

 

You can apply any filters you like in any order you like.

 

Revit may perform some optimisation by reordering them.

 

It is probably useful to apply all quick filters first:

 

http://thebuildingcoder.typepad.com/blog/2015/12/quick-slow-and-linq-element-filtering.html

 

I have repeated more than a hundred times now:

 

Using ToList is normally not necessary and may cause a significant inefficiency.

 

http://thebuildingcoder.typepad.com/blog/2012/09/findelement-and-collector-optimisation.html

 

I your case, I would simply use:

 

  string targetName = "60\" x 30\" Student";
  
  FilteredElementCollector instances
    = new FilteredElementCollector( document )
      .OfClass( typeof( FamilyInstance ) )
      .Where( x => x.Name == familySymbolName );

 

As also mentioned numerous times in the past, you can make it more efficient by using a parameter filter to check the name instead of LINQ:

 

http://thebuildingcoder.typepad.com/blog/2010/06/element-name-parameter-filter-correction.html

 

Check out all the examples provided by The Building Coder samples in the module CmdCollectorPerformance.cs:

 

https://github.com/jeremytammik/the_building_coder_samples

 

https://github.com/jeremytammik/the_building_coder_samples/blob/master/BuildingCoder/BuildingCoder/C...

 

I hope this helps.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 14

Hi Jeremy, Certainly there are many, many examples as you say, and I think I have read most of them. Regarding your suggested code:

Assume there are two title block families: TB1 and TB2, both of which has a type named 60" x 30" Student, then your collector will return instances of both TB1 and TB2 families. I thought it must be possible to write a single filter statement that would collect only TB2:60" x 30" Student instances. It seems counter intuitive to run a foreach over your results to check the family type. Again, am I missing something? Dale.




______________
Yes, I'm Satoshi.
Message 4 of 14

Dear Dale,

 

Thank you for your update.

 

Yes, of course you could filter for families first, then determine their types, and then the instances referring to those.

 

In fact, the CmdCollectorPerformance.cs module includes code demonstrating that approach.

 

However, since the elements you are after in the end are the instances, it makes sense to filter for those right away, and then eliminate the ones that don't match the expected family.

 

Please take a look at the code region to retrieve named family symbols using either LINQ or a the more efficient parameter filter in CmdCollectorPerformance.cs:

 

https://github.com/jeremytammik/the_building_coder_samples/blob/master/BuildingCoder/BuildingCoder/C...

 

It demonstrates several different approaches.

 

You can implement similar code to retrieve family instances instead of symbols.

 

Here is updated sample snippet using the simple but inefficient LINQ post-processing to achieve that:

 

  string targetFamilyName = "Family Xyz";
  string targetInstanceName = "60\" x 30\" Student";
  
  FilteredElementCollector instances
    = new FilteredElementCollector( document )
      .OfClass( typeof( FamilyInstance ) )
      .Where( x => x.Name.Equals( targetInstanceName )
      .Where( x => x.Symbol.Family.Name.Equals( targetFamilyName )

 

You could speed this up both by switching from LINQ to parameter filters for the name matching and by adding more quick filters, e.g., for the category and lots of other properties.

 

I hope this helps.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 14

Hi Jeremy, The logic of your code is what I would expect, and had tested. This is the result of using your exact example:

 

Jeremy.png




______________
Yes, I'm Satoshi.
Message 6 of 14

Dear Dale,

 

Thank you for your update.

 

Sorry for confusing you.

 

The code I posted is untested, and I should have said so.

 

Symbol is a property of FamilyInstance, and `x` is an Element, so you have to cast it:

 

 

  string targetFamilyName = "Family Xyz";
  string targetInstanceName = "60\" x 30\" Student";
  
  FilteredElementCollector instances
    = new FilteredElementCollector( document )
      .OfClass( typeof( FamilyInstance ) )
      .Where( x => x.Name.Equals( targetInstanceName )
      .Where( x => ((FamilyInstance)x).Symbol
        .Family.Name.Equals( targetFamilyName )

I assumed you knew.

 

I hope this helps.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 14

Thank you again Jeremy. There were a couple of parentheses and cast adjustments, I have included the working code for completeness, As far as assuming knowledge, remember: "Never attribute to malice that which is adequately explained by stupidity"...

 

// get all instances by family name then type name
            List<FamilyInstance> familyInstances
              = new FilteredElementCollector(pdoc)
              .OfClass(typeof(FamilyInstance))
              .Where(x => ((FamilyInstance)x).Symbol.Family.Name.Equals(pstrFamilyNameOld)) // family
              .Where(x => x.Name.Equals(pstrFamilyTypeNameOld)) // family type               
              .Cast<FamilyInstance>()
              .ToList<FamilyInstance>();

            // get titleblock family symbol (= definition) by family name then type name
            FamilySymbol fs = new FilteredElementCollector(pdoc)
                .OfClass(typeof(FamilySymbol))
                .OfCategory(BuiltInCategory.OST_TitleBlocks)
                .Where(x => ((FamilySymbol)x).FamilyName.Equals(pstrFamilyNameNew)) // family
                .FirstOrDefault(x => x.Name == pstrFamilyTypeNameNew) as FamilySymbol; // family type

 




______________
Yes, I'm Satoshi.
Message 8 of 14

Dear Dale,

Glad it helped!

 

Thank you for the corrected code and very apt (and all too frequently useful) quote!

 

Oh, and you can optimise a bit, if you are using the LINQ Cast method anyway:

 

  // get all instances by family name then type name
  List<FamilyInstance> familyInstances
    = new FilteredElementCollector(pdoc)
    .OfClass(typeof(FamilyInstance))
    .Cast<FamilyInstance>()
    .Where(x => x.Symbol.Family.Name.Equals(pstrFamilyNameOld)) // family
    .Where(x => x.Name.Equals(pstrFamilyTypeNameOld)) // family type               
    .ToList<FamilyInstance>();
  
  // get titleblock family symbol (= definition) by family name then type name
  FamilySymbol fs = new FilteredElementCollector(pdoc)
      .OfClass(typeof(FamilySymbol))
      .OfCategory(BuiltInCategory.OST_TitleBlocks)
      .Cast<FamilySymbol>()
      .Where(x => x.FamilyName.Equals(pstrFamilyNameNew)) // family
      .FirstOrDefault(x => x.Name == pstrFamilyTypeNameNew); // family type

 

No need to cast twice.

 

Like this, `x` is a FamilyInstance (or FamilySymbol) itself, directly.

 

Cheers,

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 9 of 14

Thanks Jeremy. Much appreciated. Dale




______________
Yes, I'm Satoshi.
Message 10 of 14

My pleasure, Dale.

 

Thank you for your appreciation.

 

Added to The Building Coder samples for future reference:

 

https://github.com/jeremytammik/the_building_coder_samples/commit/ca8fcace3a4c9eb7d6563736800fe79d76...

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 11 of 14

Man, I wish I'd found that a week ago.




______________
Yes, I'm Satoshi.
Message 12 of 14

That was impossible, since I only added it just now after our conversation.



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 13 of 14

Edited and published on the blog for future reference:

 

http://thebuildingcoder.typepad.com/blog/2017/08/forge-installed-version-move-group-filter-by-name.h...

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 14 of 14
Extraneous
in reply to: jeremytammik

There is an easier:

 

private List<FamilyInstance> GetInstancesBySymbol(Document doc, FamilySymbol symb)
{
	List<FamilyInstance> fams = new FilteredElementCollector(doc)
		.OfClass(typeof(FamilyInstance))
		.Cast<FamilyInstance>()
		.Where(fi => fi.Symbol.Id == symb.Id)
		.ToList();
	return fams;
}

 

 

 

Александр Зуев / Alexander Zuev
In BIM we trust
Facebook | VK | Telegram
Шаблон и обучение Revit КЖ/КМ

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community