Python: Compare list of strings to list of tuples, and create a new list based on match or no match

Question:

I have a list of strings a, and a list of tuples b where:

a=['from src1 to dest2','from src3 to dest4','from src5 to dest6']
b=[['destb','dest2'],['destd','loc4'],['destf','dest6'],['desth','dest8']]

a and b can vary in length and be unequal.

I want to iterate through a, check if ‘dest’ matches the first string in each tuple, replacing it with the second string if it does, and append it to a new list. If no match is found, I want to append the string unchanged to the list.

So it should look something like this:

newlist=['from src1 to destb','from src3 to destd','from src5 to destf']

My current code looks like this:

for source_dest_statement in source_dest_statements:
  for i,j in in source_dest_tuple:
    if j in source_dest_statement:
      amended_source_dest_statements.append('SOMETHING HAS CHANGED!')
      amended_source_dest_statements.append(re.sub(j,i,source_dest_statement))
    else:
      amended_source_dest_statements.append(source_dest_statement)
  

As expected, this repeatedly appends the currently iterated source_dest_statement when there are no matches, when I need them to only occur as many times as they were present in the original list. What would be the simplest way to fix this behaviour?

EDIT: I’ll try to explain the problem I’m trying to solve:

I have a firewall config which has a number of NAT statements. These statements have changed in the updated version of code, so where destination addresses used mapped addresses, they now use real addresses instead.

My list of tuples is a list of real address to mapped address pairs. So something like [(realaddress1,mappedaddress1),(r2,m2),(r3,m3)] and so on.

I have the old access list rulebase which I want to go through, and replace all the references to the mapped addresses with the corresponding real address.

What I’m trying to do:

  • Iterate through access list rules.
  • If a mapped address match is found, amend the rule, append to a list, insert a comment stating the rule has been modified.
  • If no match is found, append the existing rule to the list.

Answers:

EDIT :

Since you say –

Each tuple in the list of tuples needs to be searched for in the whole list of source/dest statements.

You can add the element to the result list and then keep replacing inside it as you find elements in it from the tuple. Example –

for source_dest_statement in source_dest_statements:
    amended_source_dest_statements.append(source_dest_statement)
    for i,j in in source_dest_tuple:
        if j in amended_source_dest_statements[-1]:
            amended_source_dest_statements[-1]   = re.sub(j,i,amended_source_dest_statements[-1])

Demo –

>>> import re
>>> a=['from src1 to dest2','from src3 to dest4','from src5 to dest6']
>>> b=[['destb','dest2'],['destd','loc4'],['destf','dest6'],['desth','dest8']]
>>> result = []
>>> for x in a:
...     result.append(x)
...     for i,j in b:
...             if j in result[-1]:
...                     result[-1] = re.sub(j,i,result[-1])
...
>>> result
['from src1 to destb', 'from src3 to dest4', 'from src5 to destf']

PREVIOUS ANSWER :

This is what the for..else construct is for.

You can add the statement to add the not-changed source_dest_statement into amended_source_dest_statements , in the else: part of the for loop. And in the if part inside the second for loop, you can break once you find a match and add the changed result to the list.

The else part of a for loop is only executed if you exit the for loop normally, without using a break statement, hence that would be the case when you don’t find any matches. Example –

for source_dest_statement in source_dest_statements:
    for i,j in in source_dest_tuple:
        if j in source_dest_statement:
            amended_source_dest_statements.append(re.sub(j,i,source_dest_statement))
    else:
        amended_source_dest_statements.append(source_dest_statement)

Also, as explained in comments, b is not a list of tuples, its a list of lists (Though does not really matter for your particular usecase).

Demo –

>>> a=['from src1 to dest2','from src3 to dest4','from src5 to dst6']
>>> b=[['dest2','destb'],['dest4','locd'],['dest6','destf'],['dest8','desth']]
>>> result = []
>>> for x in a:
...     for i,j in b:
...         if i in x:
...             result.append(re.sub(i,j,x))
...             break
...     else:
...         result.append(x)
...
>>> result
['from src1 to destb', 'from src3 to locd', 'from src5 to dst6']
Answered By: Anand S Kumar

If the destination in a is found in b[0], b[1] is substituted into a and the new string is added to the result. If no substitutions we’re done it outputs the original instead.

result = []
for source in a:
    # Do all replacements with a list-comprehension
    replacements = [source.replace(old,new) for old, new in b if old in source]
    # If replacements we're done add the replacements else add the source
    if replacements:
        result.extend(replacements)
    else:
        result.append(source)

On your test input it outputs this:

['from src1 to destb', 'from src3 to locd', 'from src5 to destf']
Answered By: Caleb Mauer