python - form new list from n elements to the right of a reoccurring word
Given a list of strings:
haystack = ['hay','hay','hay','needle','x','y','z','hay','hay','hay','hay','needle','a','b','c']
Question
How would I form a new list of strings that contain, say, only the three adjacent elements (to the right) of every 'needle' occurrence within haystack
?
Solution 1:
Find all the indices of "needle"
and take 3 values right the indices.
# Get all indices of "needle"
idx = [idx for idx, val in enumerate(haystack) if val=="needle"]
#idx -> [3, 11]
# Take 3 values right of each index in `idx`.
[val for i in idx for val in haystack[i: i+4]]
# ['needle', 'x', 'y', 'z', 'needle', 'a', 'b', 'c']
# want it to be a list of list
[haystack[i: i+4] for i in idx]
# [['needle', 'x', 'y', 'z'], ['needle', 'a', 'b', 'c']]
# Want to exclude the "needle"
[val for i in idx for val in haystack[i+1: i+4]]
# ['x', 'y', 'z', 'a', 'b', 'c']
Solution 2:
This is a kind of hacky solution, but it works with only one pass through the list.
it = iter(haystack)
output = [[next(it), next(it), next(it)] for s in it if s == 'needle']
# [['x', 'y', 'z'], ['a', 'b', 'c']]
This is essentially the short-form of the following:
it = iter(haystack)
output = []
while True:
try:
elem = next(it)
if elem == 'needle':
output.append([next(it), next(it), next(it)])
except StopIteration:
break
note that, in the short form, you'll get a StopIteration
error if there are fewer than three elements following a 'needle'
.
Solution 3:
A simple list comprehension with list slicing seems to work as well:
out = [haystack[i+1:i+4] for i, x in enumerate(haystack) if x == 'needle']
Output:
[['x', 'y', 'z'], ['a', 'b', 'c']]