Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Problem 5
#1
Hi everyone,

I'm having some issues with problem 5 on this site:
https://courses.cs.washington.edu/course...part1.html

So far, I've tried to implement a loop function, but I'm getting a string "out of range" error once i+1 does not exist.

def compress(z):
    
    count = 1
    i = 0
    
    
    print(len(z))
    
    for value in z:
        if z[i] == z[i+1]:
            count = count+1
            print(count)
        else:
            print("")
            
        i += 1
Reply
#2
I've figured out the out of index issue, doesn't look pretty but..

def compress(z):
    
    count = 1
    list = []
    
    i = 0
    
    
    print(len(z))
    
    for value in z:
        while i+1 < len(z):
            if z[i] == z[i+1]:
                count = count+1
                print(count)
                i += 1
        else:
            print("")
Reply
#3
What is the purpose of this line?
for value in z:
You don't ever use value for anything and I see no reason for doing this loop.

It also isn't a solution to the problem. You're supposed to insert the count in the string, not print the count. The result should be the original string with no duplicate characters in a row, and a count following each character. In their example the input is 'cooooooooooooooooolkangaroo' and the output 'c1o17l1k1a1n1g1a1r1o2'.

As for the awkward while loop, why not do this instead?
for i in range(len(z)-1):
I think this makes more sense though:
for i in range(1, len(z)):
Or even better
for c in z[1:]:
nman52 likes this post
Reply
#4
This assignment is 'fully featured' i.e. it provides three examples of expected result. Therefore first three lines you should write into your file are:

assert compress('cooooooooooooooooolkangaroo') == 'c1o17l1k1a1n1g1a1r1o2'
assert compress('aaa') == 'a3'
assert compress('')  == ''
You should define your function before these assertions. If your function output is as expected nothing happens, if not you will get AssertionError and it will point to the assertion which failed. It is the simplest way to test your solution.

There is important difference between printing and returning. Assignment states quite clearly: " takes a string as an argument and returns a new string". Your function returns None as Python returns None if function doesn't contain either return or yield.

I don't know whether it breaks terms and conditions of this assignment but I would use itertools.groupby.
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy

Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Reply
#5
from itertools import groupby



def compress(z):
  
    for key, group in (groupby(z)):
        print(key,len((list(group))))
    
    string= [zip(key, len((list(group)))]

    return(string)
This is the closest that I've gotten to the solution. But as you've pointed out, it's still not exactly correct when the assertions are placed in. The issue is that when I try to zip the key, and list(group), I get a "int object not iterable" error, and even if I get rid of this error by removing the len() within list(group), it only returns the last iterated value of the input I put in.

For example, if I put zzzzeeeefff into the function, it'd return (f, [f, f, f])
Reply
#6
First of all - it's usually not good idea to print inside the function.

I would start with defining in spoken language what I want accomplish: 'give me groups created on every change of character'. If I have these groups I would: 'give me length of every group of characters'.

I hope that following little walkthrough with explanations helps in learning process.

How to achieve objective(s) with itertools.groupby? First of all - one must understand how groupby works. So let's have a look:

from itertools import groupby

str_ = 'abbccc'             # we need 'a1b2c3' out of it
print(list(groupby(str_)))
This will give us:

Output:
[('a', <itertools._grouper object at 0x7fe334639f10>), ('b', <itertools._grouper object at 0x7fe334639ed0>), ('c', <itertools._grouper object at 0x7fe334639f50>)]
As we can see there are character and grouper object pairs. We have character - check, but instead of group of characters we have grouper object. We should consume/unpack this object to see its content, so we try this:

print([(letter, *sequence) for letter, sequence in groupby(str_)])
This will give us:

Output:
[('a', 'a'), ('b', 'b', 'b'), ('c', 'c', 'c', 'c')]
We can observe, that after unpacking we have letters instead of grouper object. This is not exactly what we want but very close. We need number of letters, not just unpacking. As grouper object is iterator it knows nothing about its length. It knows only __next__ and StopIteration. So we should consume this object to learn it's length:

print([(letter, sum(1 for i in sequence)) for letter, sequence in groupby(str_)])
This will give us something which is very close what we want - there is character and number of occurrences:

Output:
[('a', 1), ('b', 2), ('c', 3)]
But we are not there yet. We actually need string. So we adjust this code a little bit so that we will have strings instead of tuples of str and int:

print([f'{letter}{sum(1 for i in sequence)}' for letter, sequence in groupby(str_)])
Which gives us list of strings which start with letter and end with count of letter:

Output:
['a1', 'b2', 'c3']
Now we have just construct required string from list items. Canonical way is to use join(). So compress function could look like something along lines below (and it will pass assertions with flying colors).

In function there is generator expression instead of constructing list like in previous walktrough. It was just simpler to explain what is going on with lists and actually there is no point to waste memory to construct list, we need resulting string:

def compress(string):
    pairs = (f'{letter}{sum(1 for i in length)}' for letter, length in groupby(string))
    return ''.join(pairs)

assert compress('cooooooooooooooooolkangaroo') == 'c1o17l1k1a1n1g1a1r1o2'
assert compress('aaa') == 'a3'
assert compress('') == ''
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy

Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Reply
#7
Thank you ! :)
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020