Is there a better/simpler way to do this?
Can I write all the search and replace input in only one code block?
Have a look at the result of selecting all the nodes and using the âNode to Codeâ function in the right-click menu. The resulting design script might give you some ideas
Thats not it.
What I want is one code block and one string replace node. If its possible?
depending how many replacement pairs you have, you may want a dictionary
Yes, it is possible in one code block
Thanks
Personally I donât like dictionaries for string replacement, as they tend to fall apart a bit due to the in-controllable nature of strings. Case in point if you were to look for the key â000110â youâd want to return âSK1,â but since there is no exact match for â000110â in the dictionary you receive null instead. This is not only not the intended result, but it introduces a change in data type as well. Simply put, with strings I feel that there are often too many wildcards to utilize dictionaries effectively.
So that puts me back to the drawing board. Instead of chaining all the nodes together you can call the entire string replacement chain in one code block as @Ewan_Opie indicated (with some added steps to clarify):
.Even better, if you have an unknown number of replacement pairs, or you donât want to spend forever coding each possible replacement, you could utilize a custom iterative function. By iterating over the list for every input pair it changes the value sequentially. This way it doesnât matter how many replacement terms you have - in the end youâll get what youâre after:
There are a few issues with replacing the same text twice (note the space before and after " be " in my first pair - this was required to not replace the âbeâ in âbetterâ), but this should get you started anyway.
For fun and a bit of python practice, I made a script that can replace a substring with another string and can handle most situations (as far as I can tell). Also gives a list of bools whether there was something replaced or not if needed.
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
#The inputs to this node will be stored as a list in the IN variables.
def replacing(aString,replaceList,new,bools):
count = 0
if isinstance(rplce[0],list):
for r in rplce:
if aString.find(r[0]) != -1:
h = aString.replace(r[0],r[1])
new.append(h)
count +=1
bools.append(True)
else:
continue
else:
r = replaceList
if aString.find(r[0]) != -1:
h = aString.replace(r[0],r[1])
new.append(h)
count += 1
bools.append(True)
if count == 0:
new.append(aString)
bools.append(False)
strng = IN[0]
rplce = IN[1]
new = []
bools = []
if isinstance(strng,list):
for s in strng:
replacing(s,rplce,new,bools)
else:
replacing(strng,rplce,new,bools)
OUT = new,bools
replaceStrings.dyn (10.9 KB)
The final node is the one in the bottom right that has the bool list showing.
Edit: Forgot about list of lists as inputs, so will be trying to make a recursive version as well.
Now with recursion:
replaceStrings.dyn (3.5 KB)
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
#The inputs to this node will be stored as a list in the IN variables.
def replacing(aString,replaceList,new,bools):
count = 0
if isinstance(replaceList[0],list):
for r in replaceList:
if aString.find(r[0]) != -1:
h = aString.replace(r[0],r[1])
new.append(h)
count +=1
bools.append(True)
else:
continue
else:
r = replaceList
if aString.find(r[0]) != -1:
h = aString.replace(r[0],r[1])
new.append(h)
count += 1
bools.append(True)
if count == 0:
new.append(aString)
bools.append(False)
def recReplace(aString,aReplace,new,bools):
if isinstance(aString,list):
temp = []
tempb = []
for i in aString:
recReplace(i,aReplace,temp,tempb)
new.append(temp)
bools.append(tempb)
else:
replacing(aString,aReplace,new,bools)
strng = IN[0]
rplce = IN[1]
new = []
bools = []
recReplace(strng,rplce,new,bools)
OUT = new,bools
I highly doubt the efficiency of this, but it works.
Edit: Iâd like to write down the advantage of using this method, itâs very easy to add an element youâd want to replace, just add, to the list, in the case youâd want to automate the input it should work.
I did a quick fix on my previous post as I realized that if you were in the case youâd give an input that shouldnât get a replacement youâd end up with null
outputs, which of course, you donât want to happen.
I managed to give it a simple fix with Clockworks Liste.ReplaceNull
nodes. You still get warnings, but they wonât cause actual problems.
Are you certain that actually replaces parts of strings?
Ow my bad. You mean like replace the 1 in A1. If thatâs te case give me a couple minuts.
This will replace the substring
Hi @NLDaniel
Your script looks great! Iâm trying to adapt your script to be able to pull the âSearch forâ and âReplace withâ lists from an excel file but I canât get it to work.
Hereâs what I have so far:
As you can see from the watch node to the right, the script wasnât able to process the list I fed, but when I manually created a list of strings in dynamo (like your example) it worked!
I think the format of the 2nd input of the python script (which is the list of lists of strings is correctâŚ
Can you help me?
it did not work with a string list of sublists
#Parts of this script have been taken from MEPOver FilterStringContains.
import clr
import System
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
from System.Collections.Generic import List
containString = IN[0]
parametersearch = IN[1]
replaceString = IN[2]
case = IN[3]
bips = System.Enum.GetValues(BuiltInParameter)
bm = doc.ParameterBindings
bmlist = bm.ForwardIterator()
paramId = None
testout = []
while bmlist.MoveNext():
binddef = bmlist.Key
testout.append(binddef.Name)
if binddef.Name == parametersearch:
paramId = binddef.Id
break
param = None
if paramId == None:
biplist = []
for bip in bips:
try:
name = LabelUtils.GetLabelFor(bip)
except:
continue
if name == parametersearch:
biplist.append(bip)
sharedparamrule = SharedParameterApplicableRule(parametersearch)
evaluator = FilterStringContains()
parameter = None
if paramId == None and len(biplist) == 0:
coll = FilteredElementCollector(doc)
sharedparams = coll.OfClass(ParameterElement)
for param in sharedparams:
name = param.GetDefinition().Name
if name == parametersearch:
parameter = param
if parameter != None:
ruleslist = List[FilterRule]()
provider = ParameterValueProvider(parameter.Id)
filterrule = FilterStringRule(provider, evaluator, containString, case)
ruleslist.Add(filterrule)
ruleslist.Add(sharedparamrule)
paramFilter = ElementParameterFilter(ruleslist)
collector = FilteredElementCollector(doc)
collector.OfClass(FamilyInstance).WherePasses(paramFilter)
collector2 = FilteredElementCollector(doc)
collector2.OfClass(HostObject).WherePasses(paramFilter)
collector2.UnionWith(collector)
elif paramId == None:
if len(biplist) == 1:
provider = ParameterValueProvider(ElementId(biplist[0]))
filterrule = FilterStringRule(provider, evaluator, containString, case)
paramFilter = ElementParameterFilter(filterrule)
collector = FilteredElementCollector(doc)
collector.OfClass(FamilyInstance).WherePasses(paramFilter)
collector2 = FilteredElementCollector(doc)
collector2.OfClass(HostObject).WherePasses(paramFilter)
collector2.UnionWith(collector)
else:
provider = ParameterValueProvider(ElementId(biplist[0]))
filterrule = FilterStringRule(provider, evaluator, containString, case)
paramFilter = ElementParameterFilter(filterrule)
collector = FilteredElementCollector(doc)
collector.OfClass(FamilyInstance).WherePasses(paramFilter)
collector2 = FilteredElementCollector(doc)
collector2.OfClass(HostObject).WherePasses(paramFilter)
collector2.UnionWith(collector)
for i,param in enumerate(biplist):
if i == 0:
continue
provider = ParameterValueProvider(ElementId(param))
filterrule = FilterStringRule(provider, evaluator, containString, case)
paramFilter = ElementParameterFilter(filterrule)
tempcollector1 = FilteredElementCollector(doc)
tempcollector1.OfClass(FamilyInstance).WherePasses(paramFilter)
tempcollector2 = FilteredElementCollector(doc)
tempcollector2.OfClass(HostObject).WherePasses(paramFilter)
tempcollector2.UnionWith(tempcollector1)
collector2.UnionWith(tempcollector2)
else:
ruleslist = List[FilterRule]()
provider = ParameterValueProvider(paramId)
filterrule = FilterStringRule(provider, evaluator, containString, case)
ruleslist.Add(filterrule)
ruleslist.Add(sharedparamrule)
paramFilter = ElementParameterFilter(ruleslist)
collector = FilteredElementCollector(doc)
collector.OfClass(FamilyInstance).WherePasses(paramFilter)
collector2 = FilteredElementCollector(doc)
collector2.OfClass(HostObject).WherePasses(paramFilter)
collector2.UnionWith(collector)
if parameter == None and paramId == None and len(biplist) == 0:
OUT = "Does not work with non-shared family parameters"
else:
elements = collector2.ToElements()
for e in elements:
paramVal = e.LookupParameter(parametersearch).AsString()
paramValReplaced = paramVal.replace(containString, replaceString)
try:
TransactionManager.Instance.EnsureInTransaction(doc)
setParamVal = e.LookupParameter(parametersearch).Set(paramValReplaced)
TransactionManager.Instance.EnsureInTransaction(doc)
except:
OUT = "Failed"
OUT = "Success"