3
\$\begingroup\$

I want to reduce a list to its sequence. Or I could say I want to remove duplicate neighbors in a list.

This Python code does what I want.

#!/usr/bin/env python3
data = [True, False, False, False, False, True, True]
expected = [True, False, True]

result = []
for d in data:
    try:
        if result[-1] != d:
            result.append(d)
    except IndexError:
        # Happens in first iteration
        result.append(d)

print(result)

Here is a second approach with less lines:

#!/usr/bin/env python3
data = [True, False, False, False, False, True, True]
expected = [True, False, True]

result = [data[0]]
for d in data[1:]:
    if result[-1] != d:
        result.append(d)

print(result)

But I assume there is a better and more pythonic way.

EDIT: Of course this code should go into a function. But this is not the core of the question here.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

The two programs are pretty similar, just that the first skips data[0] by catching an exception, whilst the second does it by slicing it off the input.

In both cases, we have inlined the logic directly into the program; I think it would be better to create a function and give it a good name - perhaps uniq, based on the standard command that does a similar thing with lines of input. Writing a function also gives us somewhere to document its behaviour and (using doctest) to provide some simple unit tests.

When I want to transform a sequence, I turn first to itertools. In this case, I find it has a convenient groupby function that's pretty much what we want (it even mentions that it's similar to uniq). So all we have to do is to take the key element from each tuple returned by itertools.groupby(data):

import itertools

def uniq(sequence):
    '''
    Replace any repeated elements in "sequence" with a single element.
    Examples:
    >>> uniq([])
    []
    >>> uniq([True, False, False, False, False, True, True])
    [True, False, True]
    '''
    return [k for k,g in itertools.groupby(sequence)]

This can be tested in the usual way:

if __name__ == '__main__':
    import doctest
    doctest.testmod()
\$\endgroup\$
2
  • \$\begingroup\$ I do know itertools but don't understand most of it. It is hard for me to transform the description in the docs into real world problems. \$\endgroup\$
    – buhtz
    Commented Mar 10, 2023 at 8:42
  • \$\begingroup\$ Consider doing some "kata" exercises that improve your familiarity with iterators, including generators and itertools. Those are very useful concepts in Python, and practice is the best way to improve your understanding and confidence with them. Perhaps also search this site for uses of itertools that you can follow and re-create from the problem description? \$\endgroup\$ Commented Mar 10, 2023 at 9:48
4
\$\begingroup\$

Put your code in functions. Among other benefits, it's easier to test that way.

Consider using itertools.groupby. If given no key function, it organizes the data based on adjacent runs of equal values.

from itertools import groupby

def neighboring_dups_removed(data):
    return [k for k, group in groupby(data)]
\$\endgroup\$
1
  • \$\begingroup\$ I ended up making the same points in my answer (which I wrote without seeing this, but got interrupted by a meeting before I could submit it). It's good to see we were thinking along exactly the same lines. \$\endgroup\$ Commented Mar 10, 2023 at 9:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.