get the sum of differences between integers in a list

Peter Otten __peter__ at web.de
Wed Sep 21 10:42:41 EDT 2016


Daiyue Weng wrote:

> Hi, first of all, let me rephase the problem.
> 
> For an arbitrary list of integers (the integers in the list are not
> necessary to be sequential), e.g. [1,2,3,6,8,9,10,11,13],
> 
> if a set of consecutive integers having a difference of 1 between them,
> put them in a list, i.e. there are two such lists in this example,
> 
> [1,2,3],
> 
> [8,9,10,11],
> 
> and then put such lists in another list (i.e. [[1,2,3], [8,9,10,11]]). Put
> the rest integers (non-sequential) in a separated list, i.e.
> 
> `[6, 13]`.
> 
> Note that, the problem only considers sequential integers with step_size =
> 1.
> 
> I tried to use itertools.groupby and itertools.count,
> 
> from itertools import groupby, count
> lst = [1,2,3,6,8,9,10,11,13]
> c = count()
> result = [list(g) for i, g in groupby(lst, key=lambda x: x-next(c))]
> 
> but the result is close to the requirement shown as a list of lists,
> 
> [[1, 2, 3], [6], [8, 9, 10, 11], [13]]
> 
> but it didn't separate sequential lists from non-sequential ones.
> Also, I couldn't find a way to put [6] and [13] in a single list.
> 
> I have also tried to separate sequential lists from non-sequential ones,
> 
> result = [list(g) for i, g in groupby(lst, key=lambda x: x-next(c) == 1)]
> # tried to extract [1,2,3] and [8,9,10,11] from the list
> 
> or
> 
> result = [list(g) for i, g in groupby(lst, key=lambda x: x-next(c) > 1)] #
> tried to extract [6] and [13] from the list
> 
> but they all ended up with
> 
> [[1, 2, 3], [6, 8, 9, 10, 11, 13]]
> 
> So two questions here,
> 
> 1. How does itertools.groupby key function work in my case?
> 
> 2. How to improve the program to achieve my goals?

OK, you did at least try this time. So here's my attempt: 

$ cat consecutive.py     
from itertools import chain, groupby, islice, tee


def lonely(triple):
    left, value, right = triple
    if left is not None and value - left == 1:
        return False
    if right is not None and right - value == 1:
        return False
    return True


def grouped(values):
    left, mid, right = tee(values, 3)
    triples = zip(
        chain([None], left),
        mid,
        chain(islice(right, 1, None), [None])
    )
    for key, group in groupby(triples, lonely):
        yield key, (value for left, value, right in group)


def main():
    consecutive = []
    other = []

    values = [1, 2, 3, 6, 8, 9, 10, 11, 13, 17, 19]

    for is_lonely, group in grouped(values):
        if is_lonely:
            other.extend(group)
        else:
            consecutive.append(list(group))

    print("input")
    print("  ", values)
    print()
    print("consecutive")
    print("  all     ", consecutive)
    print("  shortest", min(consecutive, key=len))
    print("  longest ", max(consecutive, key=len))
    print()
    print("other")
    print("  ", other)


if __name__ == "__main__":
    main()
$ python3 consecutive.py 
input
   [1, 2, 3, 6, 8, 9, 10, 11, 13, 17, 19]

consecutive
  all      [[1, 2, 3], [8, 9, 10, 11]]
  shortest [1, 2, 3]
  longest  [8, 9, 10, 11]

other
   [6, 13, 17, 19]
$

This is not as efficient as it should be -- the gaps are calculated twice, 
the check for the first and the last position is repeated on every iteration 
lots of packing and unpacking... -- either groupby() is the wrong tool or 
I'm holding it wrong ;)




More information about the Python-list mailing list