get the sum of differences between integers in a list

Peter Otten __peter__ at web.de
Fri Sep 23 06:07:35 EDT 2016


Daiyue Weng wrote:

> i, I am new to the advanced python techniques, and by studying your code,
> I understand that when calling grouped function with values[1, 2, 3, 6, 8,
> 9, 10, 11, 13, 17, 19],
> 
> tee(values, 3) generated 3 iterators shared values
> 
> left contains [1, 2, 3, 6, 8, 9, 10, 11, 13, 17, 19],
> mid contains [1, 2, 3, 6, 8, 9, 10, 11, 13, 17, 19],
> right contains [1, 2, 3, 6, 8, 9, 10, 11, 13, 17, 19].
> 
> passing them into zip(),
> 
> chain([None], left) generated,
> 
> [1, 2, 3, 6, 8, 9, 10, 11, 13, 17, 19]
> 
> chain(islice(right,1,None), [None]) generated,
> 
> [2, 3, 6, 8, 9, 10, 11, 13, 17, 19]
> 
> zip(chain([None], left), mid, chain(islice(right, 1, None), [None])
> generated triples with,
> 
> [(1,1,2), (2,2,3), (3,3,6),...,(17,17,19)]
> 
> The question is how does triples work in groupby(triples, lonely)?
> especially within lonely(triple)?

Consecutive triples with the same value for lonely(triple) are put into the 
same group. A smaller example:

values = [1, 2, 4, 6, 8, 9]

gives the group keys (True == lonely)

[False, False, True, True, False, Fale]

and results in the groups

not loneley: [1, 2]
lonely [4, 6]
not lonely: [8, 9]
 
> e.g. for first 3 tuples (1,1,2), (2,2,3), (3,3,6), lonely(triple) will
> generated
> 
> False, False, True
> 
> how does this result work in groupby()?
> 
> and what's the necessariness of
> 
> if left is not None and value - left == 1:
>         return False

If you look only at one side the groups will become

no gap on the right: [1]
gap on the right: [2, 4, 6]
no gap on the right: [8, 9]

assuming that we declare there's no gap on the right side of the last item. 
In code:

>>> from itertools import *
>>> def triples(values):
...     a, b, c = tee(values, 3)
...     return zip(chain([None], a), b, chain(islice(c, 1, None), [None]))
... 
>>> def gap(x, y): return x is not None and y is not None and y - x != 1
... 
>>> def values(triples): return [t[1] for t in triples]
... 
>>> sample = [1, 2, 4, 6, 8, 9]
>>> [values(g) for k, g in groupby(triples(sample), lambda v: gap(*v[:2]) 
and gap(*v[1:]))]
[[1, 2], [4, 6], [8, 9]]

I'm lazy, so I continue using triples even though pairs would be sufficient 
below.

>>> [values(g) for k, g in groupby(triples(sample), lambda v: gap(*v[1:]))]
[[1], [2, 4, 6], [8, 9]]

We could use a stateful key to start a group every time we see a gap

>>> def make_key():
...     group = True
...     def key(v):
...         nonlocal group
...         if gap(*v[:2]): group = not group
...         return group
...     return key
... 
>>> [values(g) for k, g in groupby(triples(sample), make_key())]
[[1, 2], [4], [6], [8, 9]]

but this has the disadvantage(?) that every lonely value lands in a separate 
group. Your usecase could then be addressed by checking the groups' lengths:

>>> consecutive = []
>>> other = []
>>> for k, g in groupby(triples(sample), make_key()):
...     g = values(g)
...     if len(g) == 1: other.extend(g)
...     else: consecutive.append(g)
... 
>>> consecutive
[[1, 2], [8, 9]]
>>> other
[4, 6]





More information about the Python-list mailing list