run_length.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from itertools import groupby, takewhile


def encode(text):
    def runs(text):
        """Break text into unencoded runs"""
        return (list(chars) for (_, chars) in groupby(text))

    def encode_run(run):
        """Encode a raw run into compressed form e.g. AAA -> 3A"""
        return u'{}{}'.format(len(run) if len(run) > 1 else '', run[0])

    return ''.join(map(encode_run, runs(text)))


def decode(text):
    def runs(text):
        """Break text into encoded runs"""
        while text:
            number = ''.join(takewhile(lambda c: c.isdigit(), text))
            yield number, text[len(number)]
            text = text[len(number) + 1:]

    def decode_run(run):
        """Encode a compressed run into raw string e.g. 3A -> AAA"""
        number, char = run
        return char * (int(number) if number else 1)

    return ''.join(map(decode_run, runs(text)))

@StevenACoffman thinks this looks great

Comments

Using groupby on the encoding - thanks to @jhanna9 for the inspiration there!

helenst commented 17 March 2016 at 10:16 UTC

You're not logged in right now. Please login via GitHub to comment