Reputation: 1180
Given integers a, b such that a < b; and some ordered iterable sequence of integers, seq
. Determine whether a
and b
appear adjacent, in that order, anywhere in seq
The obvious first pass is :
assume a < b (if a > b, just switch values).
>>> idx = 0
>>> for i in range(0, len(l)):
... if a == l[i]:
... idx = i
...
>>> b == l[idx+1]
This feels clumsy.
For example, given
>>> [1, 2, 3, 8]
If a is 1 and b is 3, they are not adjacent, if a is 3 in b is 8, they are.
Something tells me there is a more pythonic way of doing this or that this is a well explored problem, and I am missing a clearer/cleaner way to approach it.
Upvotes: 2
Views: 1613
Reputation: 3504
Very short answer using zip:
(a, b) in zip(seq[:-1], seq[1:])
This works when you are allowed to flip
(a, b) in zip(seq[:-1], seq[1:]) or (b, a) in zip(seq[:-1], seq[1:])
To make it logically correct you can test for a in seq
first. If it passes then the previous test(s) should hold.
Some info on what the zip
is doing
>>> print seq
[1, 2, 3, 8]
>>> zip(seq[:-1], seq[1:])
[(1, 2), (2, 3), (3, 8)]
Using izip and islice from the itertools package
from itertools import izip, islice
N = len(seq)
(a, b) in izip(islice(seq, 0, N - 1), islice(seq, 1, N))
Upvotes: 3
Reputation: 2934
The problem can be broken down into two subproblems:
[a, b, c]
should be converted to [(a, b), (b, c)]
.(x, y)
is in the sequence generated in step 1.You can convert an iterable to a sequence of pairs with:
def pairs(collection):
collection = iter(collection)
a = next(collection)
for b in collection:
yield a, b
a = b
collection
can be a list or even an infinite generator.
To determine whether (x, y)
is in the output of pairs
you just need to do:
(x, y) in pairs(seq)
Upvotes: 1
Reputation: 281576
Everyone else is implementing something entirely different from what the problem definition says to implement. Specifically, we're supposed to do the following:
Determine if
a == l[i]
impliesb == l[i+1]
wherei < len(l)
That doesn't require a
or b
to even be in the list, and (if "ordered" isn't intended to mean "sorted") a
and b
could show up multiple times in the list.
To determine what the problem definition actually says to determine, we can go through all pairs of adjacent elements and test whether the implication holds:
if l and l[-1] == a:
# Problem definition doesn't say what to do.
shrug()
return all(x != a or y == b for (x, y) in zip(l[:-1], l[1:]))
If we are supposed to assume the input is sorted, we can do better with a binary search:
import bisect
if l and l[-1] == a:
# We still don't know what to do for this case.
shrug()
possible_leftmost_a_index = bisect.bisect_left(l, a)
if possible_leftmost_a_index == len(a):
# All elements of l are lower than a.
return True
elif l[possible_leftmost_a_index] != a:
# a isn't in the list.
return True
elif l[possible_leftmost_a_index+1] == b:
# The implication holds. (We don't have to look for more occurrences of a,
# because bisect guarantees no a occurrences to the left, and everything to
# the right is greater than a.)
return True
else:
return False
Upvotes: 1
Reputation: 10977
Using binary search will be fastest.
The following implementation should be rather fast, will work even if b > a
, on unsorted lists and if a
and/or b
are not present in the list (will return False
in this case):
from bisect import bisect_left
def is_adjacent(a, b, l):
ind = bisect_left(l,a)
if ind >= len(l):
return False
if l[ind] == a and l[ind+1] == b:
return True
else:
return is_adjacent(a,b,l[ind+1:])
# e.g.
is_adjacent(1, 3, [1, 2, 3, 8]) # gives False
is_adjacent(3, 8, [1, 2, 3, 8]) # gives True
Upvotes: 2
Reputation: 150011
You could iterate through pairs of the elements in the sequence and check if a pair consists of a
and b
. Using the pairwise
recipe from itertools
:
>>> from itertools import tee
>>> def pairwise(iterable):
... x, y = tee(iterable)
... next(y, None)
... return zip(x, y)
...
>>> a, b, seq = 3, 8, [1, 2, 3, 8]
>>> (a, b) in pairwise(seq)
True
This will work fast for arbitrary large sequences (unlike list slicing solutions).
Upvotes: 2
Reputation: 77850
Use the any reducer to determine whether any adjacent pair matches (a,b):
>>> seq = [1, 2, 3, 8]
>>> a = 3
>>> b = 8
>>> any((seq[i], seq[i+1]) == (a,b) for i in range(len(seq)-1))
True
>>> b = 1
>>> any((seq[i], seq[i+1]) == (a,b) for i in range(len(seq)-1))
False
>>>
Upvotes: 3