Change value if previous and next value match the given value

line_list = ["a", "b", "c", "d", "e", "f"]

search_list = [
    {
        "search": "e",
        "prev_line": "d",
        "next_line": "f",
        "replace": "EE"
    },
    {
        "search": "f",
        "prev_line": "e",
        "next_line": "",
        "replace": "FF"
    }
]

Since this code will replace e to EE, so the prev_line of f is no longer be e. I want it be e so I can replace f to FF. How to fix this?

for search_item in search_list:
    search_text = search_item.get("search")
    search_text_prev_line = search_item.get("prev_line")
    search_text_next_line = search_item.get("next_line")
    replace = search_item.get("replace")

    for i, line in enumerate(line_list):
        prev_line = line_list[i - 1] if i - 1 >= 0 else ""
        next_line = line_list[i + 1] if i + 1 < len(line_list) else ""

        if line == search_text and prev_line == search_text_prev_line and next_line == search_text_next_line:
            line_list[i] = replace

print(line_list) # ['a', 'b', 'c', 'd', 'EE', 'f']

Expected result:

['a', 'b', 'c', 'd', 'EE', 'FF']

You are sequentially updating the list, so that a replacement has effect on subsequent pattern matchings. Instead, you might want to apply replacements in a more simultaneous sense.

line_list = ["a", "b", "c", "d", "e", "f"]
search_list = [
    {
        "search": "e",
        "prev_line": "d",
        "next_line": "f",
        "replace": "EE"
    },
    {
        "search": "f",
        "prev_line": "e",
        "next_line": "",
        "replace": "FF"
    }
]

def replace(search_list, lines):
    for pat in search_list:
        if (pat['search'], pat['prev_line'], pat['next_line']) == lines:
            return pat['replace']
    return lines[0]

output = [replace(search_list, lines) for lines
        in zip(line_list, ['', *line_list[:-1]], [*line_list[1:], ''])]
print(output) # ['a', 'b', 'c', 'd', 'EE', 'FF']

Here, the second parameter of replace is a 3-tuple of lines; current line, previous line, and next line. Its role is rather obvious.

We are using list comprehension to apply replace to each "line". Here, zip generates the aforementioned 3-tuples.


A simple solution would be to search one list, and modify a copy of it.

LINE_LIST_MASTER = ["a", "b", "c", "d", "e", "f"]
line_list = LINE_LIST_MASTER[:]  # make a copy

for search_item in search_list:
    search_text = search_item.get("search")
    search_text_prev_line = search_item.get("prev_line")
    search_text_next_line = search_item.get("next_line")
    replace = search_item.get("replace")

    for i, line in enumerate(LINE_LIST_MASTER):  # search MASTER
        prev_line = LINE_LIST_MASTER[i - 1] if i - 1 >= 0 else ""
        next_line = LINE_LIST_MASTER[i + 1] if i + 1 < len(LINE_LIST_MASTER) else ""

        if line == search_text and prev_line == search_text_prev_line and next_line == search_text_next_line:
            line_list[i] = replace  # modify work list

Here is one way you can accomplish it. I avoid the issue by making a new list to which we apply the changes, so that we still have the original list to make comparisons to:

line_list = ["a", "b", "c", "d", "e", "f"]
final_list = line_list.copy()

search_list = [
    {
        "search": "e",
        "prev_line": "d",
        "next_line": "f",
        "replace": "EE"
    },
    {
        "search": "f",
        "prev_line": "e",
        "next_line": "",
        "replace": "FF"
    }
]

for search_item in search_list:
    try:
        for i in range(1, len(line_list)):
            if line_list[i-1] == search_item["prev_line"] and line_list[i+1] == search_item["next_line"]\
                and line_list[i] == search_item['search']:
                final_list[i] = search_item["replace"]
        
    except IndexError:
        if search_item["next_line"] == "" and i == len(line_list)-1 and line_list[i] == search_item["search"]\
            and search_item["prev_line"]== line_list[i-1]:
            final_list[i] = search_item["replace"]

print(final_list)

Edit: Put in a condition that I forgot at the end (checking previous).

Output:

['a', 'b', 'c', 'd', 'EE', 'FF']

If you have a similar need for the case where there is a blank prev_line, you can add another case to the except block I think.