Python: find sequential change in one member of list pairs, tell another

There should be a simpler, more pythonic way to do this.

Given this list of pairs:

pp = [('a',1),('b',1),('c',1),('d',2),('e',2)] 

How easy it is to find the first element in adjacent pairs, where the second element changes (here, from 1 to 2). So I'm looking for ['c', 'd']. Suppose that for the entire list there will be only one change in the pair [1], but it can be a string.

This code works, but it seems painfully long and cumbersome.

 for i, pair in enumerate(pp): if i == 0: pInitial = pair[0] sgInitial = pair[1] pNext = pair[0] sgNext = pair[1] if sgInitial == sgNext: sgInitial = sgNext pInitial = pNext else: pOne = pInitial pTwo = pNext x = [pOne, pTwo] print x break 

Thanks Tim

+1
source share
7 answers
 import itertools as it pp = [('a',1),('b',1),('c',1),('d',2),('e',2)] # with normal zip and slicing for a,b in zip(pp,pp[1:]): if a[1] != b[1]: x=(a[0],b[0]) print x break # with generators and izip iterfirst = (b for a,b in pp) itersecond = (b for a,b in pp[1:]) iterfirstsymbol = (a for a,b in pp) itersecondsymbol = (a for a,b in pp[1:]) iteranswer = it.izip(iterfirstsymbol, itersecondsymbol, iterfirst, itersecond) print next((symbol1, symbol2) for symbol1,symbol2, first, second in iteranswer if first != second) 

Added my readable version of the generator.

+2
source

You can try something like:

 [[pp[i][0],pp[i+1][0]] for i in xrange(len(pp)-1) if pp[i][1]!=pp[i+1][1]][0] 

(using list comprehension)

0
source

try comparing pp[:-1] with pp[1:] , something like

 [a for a in zip(pp[:-1], pp[1:]) if a[0][1] != a[1][1]] 

(first look at zip(pp[:-1], pp[1:]) to see what happens

edit:

I think you will need

 ([a[0][0], a[1][0]] for a in zip(pp[:-1], pp[1:]) if a[0][1] != a[1][1]).next() 
0
source
 >>> import itertools >>> pp = [('a',1),('b',1),('c',1),('d',2),('e',2)] >>> gb = itertools.groupby(pp, key=lambda x: x[1]) >>> f = lambda x: list(next(gb)[1])[x][0] >>> f(-1), f(0) ('c', 'd') 
0
source

Here's what (just?) With recursion:

 def first_diff( seq, key=lambda x:x ): """ returns the first items a,b of `seq` with `key(a) != key(b)` """ it = iter(seq) def test(last): # recursive function cur = next(it) if key(last) != key(cur): return last, cur else: return test(cur) return test(next(it)) print first_diff( pp, key=lambda x:x[1]) # (('c', 1), ('d', 2)) 
0
source
 pp = [('a',1),('b',1),('c',1),('d',2),('e',2)] def find_first(pp): for i,(a,b) in enumerate(pp): if i == 0: oldb = b else: if b != oldb: return i return None print find_first(pp) 
0
source
 >>> pp = [('a',1),('b',1),('c',1),('d',2),('e',2)] >>> [[t1, t2] for ((t1, v1), (t2, v2)) in zip(pp, pp[1:]) if v1 != v2] [0] ['c', 'd'] >>> 

I need this for clarity ... if you find that understanding the lists is understandable. It creates two temporary lists: pp [1:] and the result of zip (). Then he compares all neighboring pairs and gives the first change he discovered.

This similar generator expression does not create temporary lists and stops processing when it reaches the first change:

 >>> from itertools import islice, izip >>> ([t1, t2] for ((t1, v1), (t2, v2)) in izip(pp, islice(pp, 1, None)) ... if v1 != v2 ... ).next() ['c', 'd'] >>> 

All the examples on this page are more compact than if you wanted to catch errors.

0
source

All Articles