Compress String to number in python

Viewed 201

I want to convert my sequence from format(AAAABBCCdd) to (A4B2C2d2) and it is case sensitive.

This is my code but it doesn't return the number for last letter. I appreciate it if you help me fix my mistake.

#[AAABBBCCCDDDDDD] -> [A3B3C3D]
def compressStToNum(A):
    Ans = [A[0]]
    count = 0
    for letter in A:
        if letter in Ans:
            count += 1
            
        else:
            Ans = Ans + [count]
            Ans = Ans + [letter]

            count = 1

        list_of_strings = [str(s) for s in Ans]
        joined_string = "".join(list_of_strings)

    print(joined_string)

print(compressStToNum("AAABBBBCCCSSS"))

4 Answers

This works for me:

#[AAABBBCCCDDDDDD] -> [A3B3C3D5]
def compressStToNum(sequence):
    previous_letter = sequence[0]
    compressed_sequence = ''
    count = 1
    for letter in sequence[1:]+' ':
        if letter == previous_letter:
            count += 1
            continue
        
        compressed_sequence += previous_letter + str(count)
        previous_letter = letter
        count = 1
    return compressed_sequence
    

print(compressStToNum("AAABBBBCCCSSS"))

Here's a small change to fix that problem in your code but this code has the problem of not dealing with empty strings. There are much better ways to write this.

# [AAABBBCCCDDDDDD] -> [A3B3C3D] def compressStToNum(A):
def compressStToNum(A):
    Ans = [A[0]]
    count = 0
    for letter in A:
        if letter in Ans:
            count += 1
        else:
            Ans = Ans + [count]
            Ans = Ans + [letter]
            count = 1

    if count > 0:
        Ans = Ans + [count]

    list_of_strings = [str(s) for s in Ans]
    joined_string = "".join(list_of_strings)

    print(f"'{A}' -> '{joined_string}'")

    return joined_string


compressStToNum("AAABBBBCCCSSS")
compressStToNum("AAABBBBCCCS")
compressStToNum("ABC")
compressStToNum("ABBBBS")
compressStToNum("S")
compressStToNum("")

Produces this output:

IndexError: string index out of range
'AAABBBBCCCSSS' -> 'A3B4C3S3'
'AAABBBBCCCS' -> 'A3B4C3S1'
'ABC' -> 'A1B1C1'
'ABBBBS' -> 'A1B4S1'
'S' -> 'S1'

A better version:

def compress_str_to_num(string_to_compress):
    cur_letter = None
    cur_count = 0
    answer = ""
    for letter in string_to_compress:
        if letter is cur_letter:
            cur_count += 1
        else:
            if cur_count > 0:
                answer += f"{cur_letter}{cur_count}"
            cur_letter = letter
            cur_count = 1

    if cur_count > 0:
        answer += f"{cur_letter}{cur_count}"

    print(f"'{string_to_compress}' -> '{answer}'")

    return answer

compress_str_to_num("AAABBBBCCC")
compress_str_to_num("ABC")
compress_str_to_num("ABBBBS")
compress_str_to_num("S")

# These are an issue in the orginal code:

compress_str_to_num("")
compress_str_to_num("AAABBBBCCCSSSAAAA")

Produces this output:

'AAABBBBCCCS' -> 'A3B4C3S1'
'ABC' -> 'A1B1C1'
'ABBBBS' -> 'A1B4S1'
'S' -> 'S1'
'' -> ''
'AAABBBBCCCSSSAAAA' -> 'A3B4C3S3A4'

Another way using Counter in sorter version,

from collections import Counter
def compressStToNum(input):
    res = Counter(input)
    res = sorted(res.items())
    res = ''.join([f"{tups[0]}{tups[1]}" for tups in res])
    return res
print(compressStToNum("AAABBBBCCCSSS"))

There are two issues in your code:

  1. you only add the previous count when you reach a new letter but not at the end of the loop.
  2. you are comparing new letters to all preceding letters instead of just the last one which will count repeated letters as par of the current one (e.g. "AAABBAAACC" -> "A3B5C")

You could fix these issues, or you could use the zip function to obtain a list of indexes where letters are changing and then use zip again to combine those indexes into repetition numbers of the initial letter:

def compressIt(S):
    breaks = [0]+[i for (i,c0),c1 in zip(enumerate(S,1),S[1:]) if c0 != c1]+[len(S)]
    return "".join(S[s]+str(e-s) for s,e in zip(breaks,breaks[1:]))

compressIt("AAABBBCCCDDDDDD") # 'A3B3C3D6'
Related