TL;DR
import bisect
def intervalo(lista, i, j):
left = bisect.bisect_left(lista, i)
right = bisect.bisect(lista, j)
return lista[left:right]
Explanation
Notice this section of your code:
if el >= li:
pos_el1 = l.index(el)
elif el >= ls:
pos_el2 = l.index(el)
sublista = l[pos_el1:pos_el2]
The code above has 3 possibilities:
el >= li
then pos_el1
is created, because entered the 1st if
;
el >= ls
then pos_el2
is created because entered the 2nd if
;
el
is smaller than both, li
and ls
, and no variable is created because it did not enter any if
and you just breed them into these if
s.
Note that in no possible flow of this code the variables pos_el1
and pos_el2
may coexist, or only one exists, or only the other, never both are created in the same stream.
This causes the posterior line: sublista = l[pos_el1:pos_el2]
must be an error, because you are using one of the variables that must not be created.
Possible solutions:
List comprehension (#Docs)
The simplest of all could be to use a comprehensilist on filtering only items that meet its condition. As your list has the ordered values, this solution works well:
def intervalo(lista, i, j):
return [item for item in lista if i <= item <= j]
lista_inicial = [12,14,15,16,18,20,24,26,28,32,34,38]
intervalo(lista_inicial, 13, 26)
# [14, 15, 16, 18, 20, 24, 26]
If you still don’t know the concept of comprehensions, an equivalent version using for
would look like this:
def intervalo(lista, i, j):
resultado = []
for item in lista:
if if i <= item <= j:
resultado.append(item)
return resultado
lista_inicial = [12,14,15,16,18,20,24,26,28,32,34,38]
intervalo(lista_inicial, 13, 26)
# [14, 15, 16, 18, 20, 24, 26]
One can use Slices, as you did yourself, but it is necessary to treat when the searched values do not exist in the list, because if the values that limit your range do not exist in the list you will have to select the nearest index. A solution could be:
def intervalo_slices(lista, i, j):
left, right = 0, len(lista)
for index, item in enumerate(lista):
if item < i:
# se for menor que o mínimo mostra do próximo em diante
left = index + 1
if item <= j:
# Se for maior que o máximo mostra até o próximo índice
right = index + 1
return lista[left:right]
The algorithm is simple:
- I create
left
and right
with standard values 0
and the total size of the list (len(lista)
), that way if the flow doesn’t enter any of the if
s the result will be lista[0:len(lista)]
which is only a copy of the original list;
- Use
enumerate
to have the indexes of the items I am iterating and perform the calculations (similar to the i
that we see in bonds for
of other languages);
- Test in all iterations if
item
is less than the minimum.
If the condition is true, it means that the lower limit of our list is the next item, as it will be equal to or greater than i
;
- Test in all iterations if
item
is less than or equal to the maximum, if yes, the index I want is also the next, because Slices do not include the right value (ex: [0, 1, 2, 3][:2]
results in [0, 1]
);
- Finally return the Slice with the right values
If you give a print
within the if
will see that left
and right
receive values several times until the conditions are no longer true. It’s not a very optimized solution, but it’s a more "handmade" algorithm and I wanted to show what went wrong with your example with a possible solution to your line of reasoning.
For a more optimized solution in this same line of reasoning, see the next session.
Module bisect
(#Docs)
Another way to solve the problem "which index of the list should I do Slice" is to use a binary search algorithm to find in which position of the list the Slice, even if the value does not exist in this list.
The module bisect
was made to work with ordered sequences and binary search, so we can take advantage of a native python module to solve the problem with a complexity O(Log2 n) instead of O(n) like the previous problems.
For that we will use the functions bisect
and bisect_left
. Both have the same purpose and function, they serve to find out at which position of an ordered sequence a value must be inserted in order to maintain its ordering.
For example:
from bisect import bisect
lista = [0, 2, 4, 5, 8, 10]
index = bisect(lista, 3)
# 2
lista.insert(index, 3)
# lista = [0, 2, 3, 4, 5, 8, 10]
# └── posição retornada por bisect
See that bisect
returned the index 2
, which is exactly where I can enter the value 3
on my list to maintain the ordering of the same.
But what’s the point of bisect_left
?
To function bisect
and bisect_left
differ only when the value sought already exists in the sequence, that is, in the previous example could be used bisect
or bisect_left
because the result would be the same.
When we look for the index of an existing value in the sequence we have two options:
Insert into the same index as the value found (bisect_left
).
To enter the value 1
on the list [0, 1, 2, 3]
:
# ┌── valor encontrado
[0, 1, 1, 2, 3]
# └── novo valor
Insert into the next index of the value found (bisect
).
To enter the value 1
on the list [0, 1, 2, 3]
:
# ┌── valor encontrado
[0, 1, 1, 2, 3]
# └── novo valor
In summary, the difference is whether the returned index is the same as the found value or the next one. The code below illustrates the examples cited above:
from bisect import bisect, bisect_left
lista = [0, 1, 2, 3]
indice = bisect(lista, 1)
# 2
indice_left = bisect(lista, 1)
# 1
Final code using bisect
Now that you understand the concept, just use the bisect_left
to correctly pick the minimum index to use on Slice and bisect
to pick up the maximum index.
import bisect
def intervalo(lista, i, j):
left = bisect.bisect_left(lista, i)
right = bisect.bisect(lista, j)
return lista[left:right]
Completion
These are some ways to do what you want, I presented the module bisect
because besides being performatic and interesting, I think it is important for the learned to have examples of practical use.
I created this Repl.it with all examples running.
William, you know table test?
– Woss
No... I’ll read about
– Guilherme Falcão
Study Python list methods, more precisely how to pick up segments of it and, of course, the method
index()
.– Giovanni Nunes