Storing a List for multiple use

I have written a Script that uses an Annotation Element, Searchs the project for all Rooms and take Information out of the room to write into the Annotation basically a Apparment annotation. It works seamless but there is a little pitfall. When using on big projects it loads for about 10 Minutes and I know why. The problem with the script is that it works like this:

  • Take all Annotations of the current view
  • For every annotation look up all rooms
  • filter from the rooms the assigned ones
  • write data into the annotion.

The problem is that it gets all Rooms all over again and again for every single Annotation instead of a accessing a global variable that has all rooms stored. these are the lines I use inside my def:

roomCategory = Category.ByName(Räume);
rooms = ElementQueries.OfCategory(roomCategory);
roomDepartment = Element.GetParameterValueByName(rooms,Bauteil);

I would like to take them out of the def and access them as a global variable. Any suggestions?

Why write the info into the annotation? Why not just use Room tags?

Could you share the script so we can take a look?

A dictionary by element ID could work well here. Woul dneed to see more of your code to help guide you in implementing it, but effectively you build a key from the room’s identifier, and put the list of rooms into the dictionary under that key. So if the identifier was say the tenant parameter, you’d group all the rooms by tenant, build a dictionary using the sublists as the values and the unique names as the keys, and then from the annotation pull the tenant from the string, pull out the list of rooms from the dictionary, and update the values using content from the rooms.

I will share a snippet of the code which should work like this:

// element is the annotation instance
// freeNames is a comma seperated string of names of rooms that shouldnt count as area

def calcTopSize(element,freeNames) {
	appartment = Element.GetParameterValueByName(element, "Appartment");
	department = Element.GetParameterValueByName(element,"Bauteil");
	roomCategory = Category.ByName("Räume");
	rooms = ElementQueries.OfCategory(roomCategory);
	roomDepartment = Element.GetParameterValueByName(rooms,"Bauteil");


	containsDepartment = String.Contains(roomDepartment,department,true);
	roomsInDepartment = List.FilterByBoolMask(rooms,containsDepartment)["in"];

	roomTop = Element.GetParameterValueByName(roomsInDepartment,"Appartment");
	contains = String.Contains(roomTop,appartment,false);
	roomsInTop = List.FilterByBoolMask(roomsInDepartment,contains)["in"];

	roomLevels = Element.GetParameterValueByName(roomsInTop,"Ebene");
	levels = List.UniqueItems(roomLevels);

	levelString = String.TrimWhitespace(String.Replace(String.Join(",",levels),"FBOK",""));


	freeNames = String.TrimWhitespace(String.Split(freeNameString,","));

	free = List.CountTrue(List.Transpose(String.Contains( Room.Name(roomsInTop),freeNames@L1<1>,true))@L2<2>) == 1;
	wnf = Math.Sum(Math.Round(Room.Area(List.FilterByBoolMask(roomsInTop, free)["out"])*100))/100;

	parameters = ["Appartment","Fläche", "Bauteil", "Ebene"];
	values = [appartment,wnf,department,levelString];
	Element.SetParameterByName(element,parameters,values);
	return values;
};

Sadly I don’t have a Revit file with “Bauteil” or “Ebene” parameters in it, so I can’t run this. The dictionary method I mentioned above should work quite well though.

you could replace them with different parameters, I am using a German version and must say that I added Bauteil as a paramter.

could you write me an example on how the dictionary would work?

Today is Tuesday; on Tuesdays we don’t use Revit. :laughing:

In all seriousness, hopefully this example using points and dictionaries instead of rooms and annotations will help you get sorted out, as I don’t have the time to Revit model and the equivalent code to use it on.

objs = // assume my objects are your rooms
	rooms;
v1 = // assume this is pulling your first parameter value
	Math.Floor(objs.X);
v2 = // assume this is pulling your second parameter value;
	Math.Floor(objs.Y);

vSet = // merge the parameter values into one comparable
	String.Join(
		" - ",
		""+List.Transpose([v1,v2])
	);
grpSet = //group the objects by their comparable value
	List.GroupByKey(objs,vSet);

roomDict = //build a dictionary of the unique keys and groups
	Dictionary.ByKeysValues(
		grpSet["unique keys"],
		grpSet["groups"]
	);

annotationValue = //get the key from the annotations
	String.Substring(annotations[annotations.Keys],0,5);

annotationGroups = //get the collection of objects for each annotation
	roomDict[annotationValue];

annotationGroupSums = //get what you want from the objects
	Math.Sum(annotationGroups.Z);
annotationGroupCounts = //get what you want from the objects
	List.Count(annotationGroups@L2<1>);

annotationString = //build the new annotation string
	String.Replace(
		annotationValue + ": " + Math.Round(annotationGroupSums,3),
		"000",
		""
	)+ " units of elevation in " + annotationGroupCounts + " different points.";

updatedAnnotations = //update the annotation
	Dictionary.SetValueAtKeys(annotations, annotations.Keys, annotationString);

It seems to me you are overly complicating things. Could you give an example of your annotation family? Are you annotating on a floorplan?

so you dont use a def at all?

sure here is a screenshot, the script above is just an snippet

I use the script to fill all values of the annotation automaticaly for the whole project,
it shows the room number, the total flat area, and different outdoor areas, the Level is used for schedule

  • Ah so basically you ‘tag’ an entire appartment and put that stamp somewhere on your drawing or make seperate appartment drawings?
    I’ve never used Area’s but I think if you only use them for appartments they could be functional here too.

  • In that case I would create an ID code for your annotation (Element.ID can change between synchronising) and connect it to the view or appartment ID. Then sort the rooms like that and get the Element.GetParameterValue that way. This way, no view needs to be opened and data transfer should be much quicker.

  • You could also use TuneUp to detect which nodes delay the most

  • For the script you could also filter the Annotation Elements so you more manually /selectively update them, say 10 at the time. Per floor / level for instance
    OR you put a binary parameter to the annotation element that once it has ran, it puts it on completed so the next time it won’t run it again and will only update the new ones.

I could help more but I would have to see an example sheet and dyn script. Hopefully these tips will help

I do, and could have here to speed things up. However when sharing something as a teaching tool I find it best to give the person asking the question the chance to review individual steps - you can always convert to a definition once you understand the concept.

Actually it works quite well right now, I don’t want to change so much at the scripts logic. It takes the Top Number which is written into the annotation, and filters rooms which has the same value for the parameter top number. this is how it finds the right rooms.

I think the main bottle neck is that it goes through all rooms everytime it goes through an annotation instance.

thanks i believe i got it now. So I basically “Presort” all data by going through the rooms and after that i loop through all annotations and write the respective data to the annotation

1 Like

If it’s all rooms per annotation, then it seems to be a lacing problem, no?

Glad you fixed it :slight_smile:

Yep - once you’re grouped and pushed into a dictionary you use the value from the annotation to get the collection you want out of the dictionary and pull the data you want from each object.