Delete unused shared parameters?

Hi all,

I tried searching topics about shared parameters and found out that most of them are “create and add to project” my question is how about removing or deleting unused shared parameters? I’ve experienced working with the project that has the same parameter names. Any idea on how to sort parameter that has no value or just a duplicate?

Thanks in Advance.

1 Like

@interactiverendering I was searching and was about to create the same topic but found this. Any update as to a solution?
Element.Parameters yields little distinguishable info to determine is used or not.

I believe it would be a wicked problem to solve, as how does one tell if a shared parameter is not being used? A parameter such as a boolean could be on/off for example - is it not in use if it is off?

The only method I can think of is it to collect all elements, use ‘get parameter value’ and ensure there are no filled versions of the parameter. Then the user would need to elect if it is safe to remove. It seems too risky otherwise.

2 Likes

Old topic, I know. regarding this, I would like to know how I can filter out allready deleted shared parameters? Actually, if you delete them manually in revit, you will still find them with with Dynamo, there is a ton of duplicates. Would be nice if I could filter all the “deleted” shared parameters.

1 Like

I had a similar situation.
I’ve imported a few families into my project, than, after a few months deleted (replaced with family parameters) their shared parameters.
I actually used @GavC’s Crumple package for that (and Orchid)

But the parameters stuck in the project.

So i improvised, and exported all the parameters from that family (now when they are family parameters) and added them to a new script to delete the shared parameters in project:

@interactiverendering you can remove the parameters like this too, but you will probably need to write them down one by one by hand. if you don’t know their origin family

addendum:
You can create a Multiple Category Schedule, export it, and then open it with Excel.
Then copy the second row into Notepad++
Format the text as a list for dynamo (helpful: Notepad++ add to every line - Stack Overflow)
and run the above script

2 Likes

Reviving an old thread- same issue today when changing shared project parameters from NUMBER to AREA - and got :
image
the manual solution was use Snoop and look up each shared parameter (See Purge Shared Parameters From a Revit Model | OpenDefinery/Blog )
Then Select by ID and DELETE manually.

Really irritating deleting the parameter form project parameters does not delete them from the “Shared parameters”

So each one of those IDs must be deleted (search by IDs, then press DELETE). What we need is Autodesk to purge those after they have been removed from project parameters on a purge. Ideally after deleting the project parameters. It seems like a BUG IMO-

If the DYN were to delete any parameters that weren’t associated with andy project items or families- that would be it. But is that possible?

Not sure I follow what you’re after. Can you post an RVT in a new thread (linking here) with a data set to test on? Keep it to one unused shared parameter and one used shared parameter so we can confirm an initial POC.

1 Like

Not too long ago I had a similar issue and I found that if I just iterated all ParamerElements and did a string equality on the param name I could then delete both and the Shared param would be gone. Now, that doesn’t check usage, but it was much faster than hunting ids in lookup.

1 Like

I’ll learn to read one day ; )

New post with example RVT and images of process.

This is because behind the scenes the binding remains. I believe amongst other reasons this relates to a parameter being able to remain in project schedules as a field post deletion. A good proof for this is;

Add a shared project parameter
Detele it from the model
In the shared param file, change its name but not its guid (bad I know)
Add it again

Its name will still be the old one. The model remembers it despite it being deleted. As you found the only way to get rid of them is targetting that Id behind the scenes.

1 Like

Quick point of clarity - it’s not the bindings that remain, but the definition (which is defined by the shared parameter, which is defined by the GUID not the name). I believe that this is to prevent family data loss as the family elements may still need this data point to not go crazy if you do something like save out the family.

The code above (behind a spoiler as I’d qualify this as “dangerous to the point of not being recommended for for production”) should allow purging the definitions (via the shared parameter elements) if they are not in the bindings map (project parameters).

2 Likes

Hi,
an example solution with Python (only for instance parameters project), using the HasValue property

import sys
import clr
import System
from System.Collections.Generic import List

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

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

# Force close any open transactions to ensure a clean slate
TransactionManager.Instance.ForceCloseTransaction()
# Get the parameter bindings of the document
bindingMap = doc.ParameterBindings
iterator =  doc.ParameterBindings.ForwardIterator()
def_toRemove = []
result = []
while iterator.MoveNext():
	definition = iterator.Key
	binding = iterator.Current
	if definition.IsValidObject:
		if isinstance(binding, InstanceBinding):
			categories = binding.Categories
			 # Create a filter to select elements in the categories of the binding
			filterCats = ElementMulticategoryFilter(List[ElementId]([cat.Id for cat in categories]))
			# Create a predicate to filter elements that have a value for the current parameter definition
			filterByHasValue = System.Predicate[System.Object](lambda x : x.get_Parameter(definition) is not None and x.get_Parameter(definition).HasValue)
			# Find all elements that have a null value for the current parameter definition
			instanceNullValues = List[DB.Element](FilteredElementCollector(doc).WherePasses(filterCats).WhereElementIsNotElementType().ToElements()).FindAll(filterByHasValue)
			if len(instanceNullValues) == 0:
				def_toRemove.append(definition)
# Start a transaction to remove the parameter definitions			
t = Transaction(doc, "Remove Parameters")
t.Start()
for def_ in def_toRemove:
	#
	if hasattr(def_, "Id"):
		filterbyId = System.Predicate[System.Object](lambda x : x.Id == def_.Id)
		# for all Parameters
		#param_elem = List[DB.Element](FilteredElementCollector(doc).OfClass(ParameterElement).WhereElementIsNotElementType().ToElements()).Find(filterbyId)
		# for only SharedParameters
		param_elem = List[DB.Element](FilteredElementCollector(doc).OfClass(SharedParameterElement).WhereElementIsNotElementType().ToElements()).Find(filterbyId)
		if param_elem is not None:
			doc.Delete(param_elem.Id)
			result.append("Project Parameter '{}' removed".format(def_.Name))

t.Commit()	
t.Dispose()
OUT = result
3 Likes

I have a working DYN with an output window here:
Parameter-Purge-Unused.dyn (15.4 KB)

the core “py.SharedParamPurge.py”:

import clr
# Import DocumentManager and TransactionManager
#https://forum.dynamobim.com/t/zombie-parameters-linger-after-delete-from-project-parameters/87934/4
#Jacob Small 2023-03-28
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
# Import relevant sections of the RevitAPI
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import Document, SharedParameterElement, FilteredElementCollector
doc = DocumentManager.Instance.CurrentDBDocument #the active document 
TransactionManager.Instance.EnsureInTransaction(doc)# Start Transaction
FilterPrefix=IN[0]
FilterPrefix_ForcePurge=IN[1]

bindings = doc.ParameterBindings #get all the parameter bindings in the document
sharedParameters = FilteredElementCollector( doc ).OfClass(SharedParameterElement).ToElements() #get all shared parameters in the document
results = []                            #an empty list for our results
for sp in sharedParameters:             #for eveyr shared parameter perfor the following
    spDef = sp.GetDefinition()          #get the definition of the shared parameter
    test = bindings.Contains(spDef)     #test if the bindings map contains the defintion
    name = sp.Name                      #get the name of the shared paramter
    for Fpre in FilterPrefix:           #For each prefix matching in list
        ParamDelete = False
        if name[:len(Fpre)]==Fpre:    #Filter out prefixes
            if not test:                #if the definition was not in the bindings map
                ParamDelete = True
                results.append("--Deleted : {0}".format(name)) #let us know the parameter has been deleted
            else:
                results.append("___In Use : {0}".format(name)) #let us know the parameter is in use
                
        if len(FilterPrefix_ForcePurge) > 0 : ##items in list
            for FpreForce in FilterPrefix_ForcePurge :
                if FpreForce>"" and name[:len(FpreForce)]==FpreForce:
                    ParamDelete = True
                    results.append("-DelForce : {0}".format(name)) #let us know the parameter has been deleted  
        
        if ParamDelete:                 ##Delete Parameters
            ParamDelete = False
            ##doc.Delete(sp.Id)           #delete the shared parameter

TransactionManager.Instance.TransactionTaskDone()# End Transaction
OUT = results                           #return the results to Dyanmo environment

and the Py.Dialog.Py:

#2023-Ron E. Allen simple form
import clr
import sys

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

import System
from System import Drawing
import System.Drawing
import System.Windows.Forms

from System.Drawing import *
from System.Windows.Forms import *
import math

##https://forum.dynamobim.com/t/reporting-to-user-at-the-end-of-dynamo-player-script/37421/6
from System.Windows.Forms import Form,Label,Button,FormBorderStyle,FormStartPosition
text="\r\n".join(IN[0])           #String each list item together with a return wrapline

#if IN[1] > 0 :                      ##Form Width input 1
fWide=IN[1]                     ##Use it
#else:
#    fWide=int(400)                       ##Use default
# 
#if IN[2] > 0 :
fHigh=IN[2]                     ##Form Height input 2
#else:
#    fHigh=int(800)                       ##Use default
# 
#if IN[3] > "" :
title=IN[3]                     ##Form Height input 2
#else:
#    title="Result:" 
#################################################################################
class popup(Form):
    ##      
    def __init__(self,text):
        self.InitializeComponent(text)
        
    def InitializeComponent(self,text): 
        #form = Form() 
        self.ClientSize = System.Drawing.Size(fWide, fHigh)                 ##Height
        self.Text = title           ##Form Title
    
        self.FormBorderStyle  = FormBorderStyle.FixedDialog ##Remove the maximize box.
        self.MaximizeBox = False            ## Set the MinimizeBox to false to remove the minimize box.
        self.MinimizeBox = False            ## Set the accept button of the form to button1.
        self.StartPosition = FormStartPosition.CenterScreen
       
        ########Label for text#####
        
        self.textbox = TextBox()
        self.textbox.AutoScroll = True
        self.textbox.ScrollBars = ScrollBars.Vertical
        self.textbox.Parent = self 
        self.textbox.Text = text 
        ##self.textbox.TextAlign = ContentAlignment.TopRight
        self.textbox.Font = System.Drawing.Font("Tahoma", 8, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0)
        
        self.textbox.Width = self.Width-20
        self.textbox.Height = self.Height-50
       
        self.textbox.Size = Size(self.Width-20, self.Height-50)
        
        self.textbox.ReadRight = False
        self.textbox.AutoSize = False
        self.textbox.Multiline  = True
        
        ##self.Controls.Add(self.label)
        ##self.Controls.Add(self.textbox)

        ##This was the key to wrapping hte text inside the lable on the form:
        ##https://stackoverflow.com/questions/1204804/word-wrap-for-a-label-in-windows-forms
        #self.textbox.MaximumSize  = System.Drawing.Size(self.Width-40,0)
        self.textbox.WordWrap=True
        
        self.textbox.Left=0
        self.textbox.Top=0
        self.ResumeLayout(False)


oForm=popup(text)    ##Set the form with the text value
oForm.ShowDialog()              ##Show the form

@dbrokaw - this is the script I thought might be tweaked for the “Schema” issues. If we delete out the schemas- they shoudl repopulate : )

1 Like

You forgot about the ID of the parameter it should be deleted as well.
I saw this trick somewhere on the internet (not sure if its totally safe was mentioned)
Just copy the ID in the Revit warning, use Select by id in the (Revit)menu
Delete.

Can you elaborate?

This delete the ID. Before the issue was deleting the parameter but the ID remained, preventing loading of an updated GUID of the same name… I.e. in cases where changing user modifiable, description, visibility… Has to be deleted. Then reloaded. This fixes that issue :upside_down_face: otherwise it complains the parameters are already defined.

Oops, you had mentioned opendefinery already