-1

Given this sorted array:

>>> x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']

I want to slice up this array so that there are always 5 elements. 2 above and 2 below. I went with:

>>> [x[i-2:i+2] for i, v in enumerate(x)]

This results in:

[[], [], ['a', 'b', 'c', 'd'], ['b', 'c', 'd', 'e'], ['c', 'd', 'e', 'f'], ['d', 'e', 'f', 'g'], ['e', 'f', 'g', 'h'], ['f', 'g', 'h', 'i'], ['g', 'h', 'i', 'j'], ['h', 'i', 'j', 'k'], ['i', 'j', 'k', 'l'], ['j', 'k', 'l']]

The problems with this are:

  1. There are 4 elements per group, not 5
  2. Not every group has 2 above and 2 below.
  3. The first and last groups are special cases. I do not want blanks at the front. What I want to see is ['a', 'b', 'c', 'd', 'e'] as the first group and then ['b', 'c', 'd', 'e', 'f'] as the second group.

I also played around with clamping the slices.

First I defined a clamp function like so:

>>> def clamp(n, smallest, largest): return max(smallest, min(n, largest))

Then, I applied the function like so:

>>> [x[clamp(i-2, 0, i):clamp(i+2, i, len(x))] for i, v in enumerate(x)]

But it didn't really work out so well:

[['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd'], ['b', 'c', 'd', 'e'], ['c', 'd', 'e', 'f'], ['d', 'e', 'f', 'g'], ['e', 'f', 'g', 'h'], ['f', 'g', 'h', 'i'], ['g', 'h', 'i', 'j'], ['h', 'i', 'j', 'k'], ['i', 'j', 'k', 'l'], ['j', 'k', 'l']]

Am I even barking up the right tree?

I found two SO articles about this issue, but they didn't address these edge cases:

Search a list for item(s)and return x number of surrounding items in python

efficient way to find several rows above and below a subset of data

101010
  • 14,866
  • 30
  • 95
  • 172
  • Are you looking for something like this `[x[i-2:i+3] for i in range(2, len(x) - 2)]` Also why not just do 4 elements after the subject? `[x[i:i+5] for i in range(len(x) - 4)]` – alec Feb 21 '20 at 05:00

1 Answers1

0

A couple of observations:

you may want to use range(len(x)) instead of enumerate, then you will avoid having to unpack the result.

If anyone need to understand slice notation, this may help

Then, you can filter the list inside the comprehension

x = list('abcdefghijklmno')
[ x[i-2:i+2+1] for i in range(len(x)) if len(x[i-2:i+2+1]) == 5 ]
# [['a', 'b', 'c', 'd', 'e'], ['b', 'c', 'd', 'e', 'f'], ['c', 'd', 'e', 'f', 'g'], ['d', 'e', 'f', 'g', 'h'], ['e', 'f', 'g', 'h', 'i'], ['f', 'g', 'h', 'i', 'j'], ['g', 'h', 'i', 'j', 'k'], ['h', 'i', 'j', 'k', 'l'], ['i', 'j', 'k', 'l', 'm'], ['j', 'k', 'l', '`
# On python 3.8 you can use the walrus operator!!!
[ y for i in range(len(x)) if len(y:=x[i-2:i+2+1]) == 5 ]
# [['a', 'b', 'c', 'd', 'e'], ['b', 'c', 'd', 'e', 'f'], ['c', 'd', 'e', 'f', 'g'], ['d', 'e', 'f', 'g', 'h'], ['e', 'f', 'g', 'h', 'i'], ['f', 'g', 'h', 'i', 'j'], ['g', 'h', 'i', 'j', 'k'], ['h', 'i', 'j', 'k', 'l'], ['i', 'j', 'k', 'l', 'm'], ['j', 'k', 'l', 'm', 'n'], ['k', 'l', 'm', 'n', 'o']]
Javier JC
  • 309
  • 2
  • 3