String replace

Is there a better/simpler way to do this?
Can I write all the search and replace input in only one code block?

image

1 Like

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 :wink:

2 Likes

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

2 Likes

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.

3 Likes

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.

1 Like

Now with recursion:
replaceStrings.dyn (3.5 KB)
Capture2

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
3 Likes


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.

1 Like

image
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.

@moeng

I think you want something like this.

2 Likes

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

moeng1

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"
	

image

1 Like