How to replace multiple strings in a list with sublists

How to replace multiple strings in a list with sublists?

I am trying to replace a list composed of 8 strings if they are contaned into a list of many sublists of strings. I found useful the clockwork package node Replace.Multiple but I would like to do it with designscript or python.

it works sometimes…but the custom node takes quite a lot time to replace strings, then looking for alternative

this example does not work, I thought it does not work with Empt Lists and Nulls but it works within a custom node package…so I guess it does not work with a list of sublists strings as input
image
image
the code:

import clr
import re

def ReplaceMultiple(str, searchstr, replacestr, sort):
	replacements = dict(zip(searchstr, replacestr))
	if sort: substrs = sorted(replacements, key=len, reverse=True)
	else: substrs = searchstr
	regexp = re.compile('|'.join(map(re.escape, substrs)))
	return regexp.sub(lambda match: replacements[match.group(0)], str)
	
if isinstance(IN[1], list): searchstr = IN[1]
else: searchstr = [IN[1]]
if isinstance(IN[2], list): replacestr = IN[2]
else: replacestr = [IN[2]]

if isinstance(IN[0], list): OUT = [ReplaceMultiple(x, searchstr, replacestr, False) for x in IN[0]]
else: OUT = ReplaceMultiple(IN[0], searchstr, replacestr, False)

Can you share an image of the structure of the string list you wish to replace within?

Hard to know how to script it without seeing the structure you’re looking at.

2 Likes

There have been a couple similar topics posted in just the last few days. Have you checked them out yet?

Aren’t all ClockWork nodes just Python scripts in a Custom Node?

1 Like

That seems to be correct based on the error. Have you tried using list levels with the node?

1 Like

You mean something like this?

// Search the texts
texts: string[][];

// Search pattern that must be replaced
searchtexts: string[];

// Replace pattern
replacetexts: string[];


// Function to replace multiple texts
[Imperative]
{
  result = [];

  // Loop through all texts
  for (i in 0..List.Count(texts)-1)
  {
    currenttext = texts[i];

    // Loop through the search patterns that must be replaced
    for (j in 0..List.Count(searchtexts)-1)
      {
        currenttext = String.Replace(currenttext, searchtexts[j], replacetexts[j]);
      }

      // Add it to the result
      result[i] = currenttext;
  }

  return result;
};
2 Likes

some error, it works with DSCore.String.

why is not that possible to achieve with the OOTB node String.Replace? the code seems to do same

You probably have other packages that contain a String object. In that case you must point to DSCore.String to use the ootb functions.

I agree.
@ruben.romero, what you’re asking is probably perfectly possible but we can’t really figure out what you’re trying to achieve like this. Can you just show an example of what your input list looks like and what you want your output list to look like?

2 Likes

Hi,
Here is a variation of the python code (1st post)

import sys
import clr
import re
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

def replacetext(text):
    global dict_replace
    if text is not None:
        rep = dict((re.escape(k), v) for k, v in dict_replace.items()) 
        pattern = re.compile("|".join(rep.keys()))
        text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
    return text


def nestedRecursFunc(lst, functoApply):
    newlst = list(lst)[:] # copy input list and convert it (if necessary)
    for idx, elem in enumerate(lst):
        if isinstance(elem, list) and len(elem) > 0:
            newlst[idx] = nestedRecursFunc(lst[idx], functoApply)
        elif isinstance(elem, list) and len(elem) == 0:
            newlst[idx] = []
        else:
            newlst[idx] = functoApply(lst[idx])

    return newlst
    
multi_NestedList = IN[0]
dict_replace = dict(IN[1])

OUT = nestedRecursFunc(multi_NestedList, replacetext)
1 Like

hello, the python code did not work because this error warning. the designscript worked well.
image

@PauLtus something like this, @Anton_Huizinga I tried python and designscript, and the python of custom node was double faster with many items in the string list input

1 Like

This should work.

Python Code
subs = IN[0]
searchStr = IN[1]
replaceStr = IN[2]
out = []

for sub in subs:
    output = []
    for s in sub:
        for search,replace in zip(searchStr,replaceStr):
            s = s.replace(search,replace)
        output.append(s)
    out.append(output)

OUT = out
3 Likes

Your list structure is kinda weird, if it’s always like this, it’s okay but if you want that to work consistently it might actually be the easiest to string together a bunch of String.Replace nodes.

Anyway, a bit of Python is still more elegant so here we go:

txtlist = IN[0]
oldlist = IN[1]
replist = IN[2]

outlist=[]
for txt1 in txtlist:
	out = []
	for txt in txt1:
		for old,rep in zip(oldlist,replist):
			txt.replace(old,rep)
		out.append(txt)
	outlist.append(out)

OUT = outlist

Edit: @Nick_Boyts had the exact same idea, it seems.

1 Like

many thanks @Nick_Boyts @Anton_Huizinga @PauLtus @c.poupin , I also tried that code which I found in other forum posts before write my post initially and I remember to have a warning message in the python node. Now I tried and it works fine, I did a comparison between the solution with Designscript as well and you can check run time results, Python wins.

thanks great answers to everyone, but disappointed with the python I wrote even looking smart and the @c.poupin version as well.

Python is almost always going to win in a head-to-head comparison. Especially with looping.

3 Likes

hello, in advance sorry for the digression
Depending on what you “claim”, it is more efficient to switch to Python as soon as you have loops rather than stay in Designscript,
where it was humorous (note for jokes.)

Cordially
christian.stan