1

The first function is able to separate each letter of a string and list how many times that letter appears. For example:

print(rlencode("Hello!"))
[('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]

How do I get rldecode(rle): do the the complete opposite of rlencode(s) so that rldecode(rlencode(x)) == x returns True

def rlencode(s):
    """
    signature: str -> list(tuple(str, int))
    """
    string=[]
    count=1
    for i in range(1,len(s)):
        if s[i] == s[i-1]:
            count += 1
        else:
            string.append((s[i-1], count))
            count=1
        if i == len(s)-1:
            string.append((s[i], count))
    return string



def rldecode(rle):
    """
    #signature: list(tuple(str, int)) -> str
    #"""
    string=" "
    count=1
    for i in rle:
        if i == rle:
            string += i
    return string
robotHamster
  • 609
  • 1
  • 7
  • 24
jfisk
  • 49
  • 1
  • Your output does not contain information about ordering. Your 'relencode` function needs to output something more like `list(tuple(str, int, [positions]))` in order to be able to reconstruct. Do I seem to be following what you're asking for here...? – robotHamster Nov 09 '18 at 02:10
  • If an answer on the page has satisfied what you need, please make sure you pick one :) – robotHamster Nov 12 '18 at 00:52

5 Answers5

1

You can use the fact that you can multiply a string by a number to repeat it and use `''.join() to bring the elements of the list together.

To show the effect of string multiplication, I multiplied "a" by 5

"a"*5 #'aaaaa'

Using that in a comprehension will give you

str = [char[0]*char[1] for char in rle] #['H', 'e', 'll', 'o', '!']

Then add in the ''.join() and you have your answer.

l = [('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
str = ''.join(char[0]*char[1] for char in rle) #'Hello!'

So your function would be

def rldecode(rle):
    """
    signature: list(tuple(str, int)) -> str
    """
    return ''.join(char[0]*char[1] for char in rle) 

Also, if you would like to make your rlencode a little cleaner, you can simplify it a little bit by using enumerate to help you keep your position in the string and check if you're about to hit either a new character or the end of the string. You just have to increment the counter on each loop.

def rlencode(s):

    output = []
    count = 0

    for i, char in enumerate(s):

        count += 1

        if (i == (len(s)-1)) or (char != s[i+1]):
            output.append((char, count))
            count = 0

    return output
Cohan
  • 4,384
  • 2
  • 22
  • 40
  • I like this, but it would be a but more readable if you used something other than lowercase "L" as your list variable and stayed away from using variables like `str` that can be confused with built-ins. – benvc Nov 09 '18 at 02:18
  • Appreciate the feedback. I changed it to `rle` since that's the OP's variable. Also changed `c` to `char` for legibility. – Cohan Nov 09 '18 at 02:21
  • I like the answer and it works for OPs particular input, but wouldn't it make more sense to have `rlencode` and `rldecode` preserve ordering for inputs with non-consecutively repeated characters? (I'm trying to get a little attention for my answer xD) – robotHamster Nov 09 '18 at 03:41
  • It all depends on what the OP is looking for. Your algorithm might save a bit of storage space on longer strings, but that's the only benefit from it. Your code is much more difficult to read for both the programmer and the computer, as is the output, and as you indicated, your algorithm is quite computationally expensive. Running it 10 times on 5 paragraphs of lorem ipsum, my code averaged 0.0178s while yours averaged 0.0255s. – Cohan Nov 09 '18 at 04:09
  • Much appreciated! @BrianCohan – robotHamster Nov 09 '18 at 06:02
0

Use join:

b = [('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
''.join([c[0] * c[1] for c in b])
Hello!

You can also use list comprehensions for your initial function.

Alex
  • 6,610
  • 3
  • 20
  • 38
  • 1
    looks ok. You could also get rid of those ugly indices by using tuple unpacking like `''.join(c * n for c, n in L)` – Paul Rooney Nov 09 '18 at 02:21
0

You can use collections.Counter.elements():

from collections import Counter
l = [('H', 1), ('e', 1), ('l', 2), ('o', 1), ('!', 1)]
print(''.join(Counter(dict(l)).elements()))

This outputs:

Hello!
blhsing
  • 91,368
  • 6
  • 71
  • 106
0

A simple, readable solution is to iterate over all of the tuples in the list returned by rlencode and construct a new string from each letter (and it's frequency) like so:

def rldecode(rle):
    string = ''
    for letter, n in rle:
        string += letter*n
    return string
Samantha
  • 275
  • 1
  • 12
-1

An answer that's easy to read but also accounts for ordering in the problem:

def rlencode(s):
    """
    signature: str -> list(tuple(str, int, list(int)))
    """
    result=[]
    frequency=1
    for i in range(len(s)):
        letters = [item[0] for item in result]
        if s[i] in letters:
            idx = letters.index(s[i])
            frequency=result[idx][1]
            frequency+=1
            positions= result[idx][2]
            positions.append(i)
            result[idx] = (s[i],count,lst)
        else:
            result.append((s[i],1,[i]))
    return result




def rldecode(rle):
    """
    #signature: list(tuple(str, int, list(int))) -> str
    #"""

    frequencies = [i[1] for i in rle]
    total_length = sum(frequencies)
    char_list=[None]*total_length
    for c in rle:
        for pos in c[2]:
            char_list[pos] = c[0]
    return "".join(char_list)


text = "This is a lot of text where ordering matters" 
encoded = rlencode(text)
print(encoded)
decoded = rldecode(encoded)
print(decoded)

I adapted it from the answer posted by @Brian Cohan

It should be noted that the answer is computationally expensive because of .index() if letter grows really long as explained in this SO post

robotHamster
  • 609
  • 1
  • 7
  • 24