Ignoring Changed Index Check (Python)

Question:

I have made a script:

our_word = "Success"

def duplicate_encode(word):
    char_list = []
    final_str = ""
    changed_index = []
    base_wrd = word.lower()

    for k in base_wrd:
        char_list.append(k)

    for i in range(0, len(char_list)):
        count = 0
        for j in range(i + 1, len(char_list)):
            if j not in changed_index:
                if char_list[j] == char_list[i]:
                    char_list[j] = ")"
                    changed_index.append(j)
                    count += 1
                else:
                    continue

        if count > 0:
            char_list[i] = ")"
        else:
            char_list[i] = "("
        print(changed_index)
        print(char_list)

    final_str = "".join(char_list)
    return final_str

print(duplicate_encode(our_word))

essentialy the purpose of this script is to convert a string to a new string where each character in the new string is "(", if that character appears only once in the original string, or ")", if that character appears more than once in the original string. I have made a rather layered up script (I am relatively new to the python language so didn’t want to use any helpful in-built functions) that attempts to do this. My issue is that where I check if the current index has been previously edited (in order to prevent it from changing), it seems to ignore it. So instead of the intended )())()) I get )()((((. I’d really appreciate an insightful answer to why I am getting this issue and ways to work around this, since I’m trying to gather an intuitive knowledge surrounding python. Thanks!

Asked By: CGI-Demon402

||

Answers:

word = "Success"
print(''.join([')' if word.lower().count(c) > 1 else '(' for c in word.lower()]))
Answered By: Alex

The issue here has nothing to do with your understanding of Python. It’s purely algorithmic. If you retain this ‘layered’ algorithm, it is essential that you add one more check in the "i" loop.

our_word = "Success"

def duplicate_encode(word):
    char_list = list(word.lower())
    changed_index = []

    for i in range(len(word)):
        count = 0
        for j in range(i + 1, len(word)):
            if j not in changed_index:
                if char_list[j] == char_list[i]:
                    char_list[j] = ")"
                    changed_index.append(j)
                    count += 1
                    
        if i not in changed_index:    # the new inportant check to avoid reversal of already assigned ')' to '('            
            char_list[i] = ")" if count > 0 else "("

    return "".join(char_list)

print(duplicate_encode(our_word))
Answered By: Alex

Your algorithm can be greatly simplified if you avoid using char_list as both the input and output. Instead, you can create an output list of the same length filled with ( by default, and then only change an element when a duplicate is found. The loops will simply walk along the entire input list once for each character looking for any matches (other than self-matches). If one is found, the output list can be updated and the inner loop will break and move on to the next character.

The final code should look like this:

def duplicate_encode(word):
    char_list = list(word.lower())
    output = list('(' * len(word))
    for i in range(len(char_list)):
        for j in range(len(char_list)):
            if i != j and char_list[i] == char_list[j]:
                output[i] = ')'
                break
    return ''.join(output)

for our_word in (
    'Success',
    'ChJsTk(u cIUzI htBp@qX)OTIHpVtHHhQ',
    ):
    result = duplicate_encode(our_word)
    print(our_word)
    print(result)
    

Output:

Success
)())())
ChJsTk(u cIUzI htBp@qX)OTIHpVtHHhQ
))(()(()))))())))()()((())))()))))
Answered By: ekhumoro