#### [SOLVED] Pyhonic use of if statements for inequalities applied to chunked list

Given the following lists in Python:

``````l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
v1 = [1,2,3,4,5,6,7] #7 values
v2 = [a,b,c,d,e,f,g] #7 letters
``````

I want to check whether a number is contained in any interval, and return another value based on which interval this is.

Example:

``````1. My test value is 1111
2. It belongs to the second interval: 1000 < 1111 < 5000
3. Hence I need to return b
``````

I would address the problem by:

1. Creating chunks of `l1`
2. Iterating over each chunk
3. Writing one if statement for each chunk
4. Return the letter corresponding to the correct chunk

I can create chunks of it by looking at every consecutive pair of numbers:

``````def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq)))

for group in chunker(l1, 2):
print(group)
``````

This returns:

``````[0, 1000]
[1000, 5000]
[5000, 10000]
[10000, 20000]
[20000, 30000]
[30000, 40000]
[40000, 50000]
[50000]
``````

My questions:

1. Is there a Pythonic way of writing those if statements instead of having one for each chunk? What if I have 1,000 chunks?
2. If there is, how to deal with the last chunk without creating a specific case for it?

#### @r.ook 2019-10-09 14:49:50

You can just use a generator to get the result:

``````l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']

value = 1111

result = next((v for v, i, j in zip(v2, l1[:-1], l1[1:]) if value in range(i, j)), None)
``````

Output:

``````>>> result
'b'
``````

By adding a default of `None` as second argument of `next()`, you can handle cases when not found as well:

``````value = -50

result = next((v for v, i, j in zip(v2, l1[:-1], l1[1:]) if value in range(i, j)), None)

>>> result
# None
``````

If you just wanted the index, you can use `enumerate` instead:

``````result = next((v for v, (i, j) in enumerate(zip(l1[:-1], l1[1:])) if value in range(i, j)), None)

>>> result
# 1
``````

Explanation:

The result line consists of a few parts:

``````next(iterator [, default])
``````

This is a function to retrieve the next item in the `iterator` passed in the argument. The `default` serves to return a default value if a `StopIteration` is encountered. The `iterator` in question is the generator here (broken down for clarity):

``````(
v                                  # point 4
for v, i, j                      # point 2
in zip(v2, l1[:-1], l1[1:])    # point 1
if value in range(i, j)          # point 3
)
``````
1. The `zip` function collates the `lists` that were passed, so that `v2[0]`, `l1[:-1][0]` and `l1[1:][0]` forms into a tuple `('a', 0, 1000)`, and so on for each index.

2. The `for v, i, j` serves to extract the elements within the tuple.

3. The `if value in range(i, j)` serves to check that `1111` is within the range between `range(0, 1000)`.

4. If it's a match, `v` is returned. If not, continue to the next iteration.

#### @Zizzipupp 2019-10-09 14:51:32

Rather than `b`, it should return its index, e.g. `1`. Also, it would be great if you could explain the result line :)

#### @r.ook 2019-10-09 14:54:49

That's even simpler then, you don't even need `v2` at all? `next((v for v, (i, j) in enumerate(zip(l1[:-1], l1[1:])) if value in range(i, j)), None)`

#### @Albin Paul 2019-10-09 14:15:17

You can `zip` list `l1` and `l1[1:]` which will make the intervals in a fashion that you need. And for some extra pointers you can incorporate binary search into this since the intervals are sorted, that I'll leave for you to optimise. The current runtime of this algorithm is `O(n)` you can reduce the runtime to `O(log(n))`.

``````from __future__ import print_function
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
interval = []
letter = None
for index,(left,right) in enumerate(zip(l1,l1[1:])):
if left <= element <= right:
interval = [left,right]
letter = index
break
if letter:
print("interval is" ,interval)
else:
print("No interval found")
``````

EDIT

I have added a algorithm using the `bisect` module in python it produces the same output but uses binary search hence it should be faster and runs in `O(log(n))`

``````from __future__ import print_function
import bisect
li = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
index = bisect.bisect(li, element)
if index == 0 or index == len(li):
print("No interval found")
else:
print("interval is",li[index - 1], li[index])
``````

OUTPUT

``````answer is  b
interval is [1000, 5000]
``````

#### @Albin Paul 2019-10-09 14:22:53

@Frank please tell which portion did you find hard to read. I`ll try my best to improve

#### @Frank 2019-10-09 14:44:35

mostly "index,(left,right) in enumerate(zip(l1,l1[1:]))". enumerate a zip to integer and tuple of two integers makes me need a brain specialist :-D I'm also not a big friend of break inside a for loop. Don't guess me wrong. That is only my opinion. Code that works... works.

#### @Albin Paul 2019-10-09 14:56:27

@Frank I have added a second algorithm an improved one if I may say so myself.

#### @Frank 2019-10-09 14:12:43

This works only with chunks of len 2.

``````l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
# v1 = [1,2,3,4,5,6,7] #7 values
v2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g']  # 7 letters

def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq)))

def chunk_with_value(list_of_chunks, value):
"""returns the chunk if the value is inside the range"""
for chunk in list_of_chunks:
if chunk[0] < value < chunk[1]:
return chunk

def chunk_to_letter(value_list, letter_list, chunk):
"""returns the letter based on the index of the first chunk element"""
for i, value in enumerate(l1):
if value == chunk[0]:
return v2[i]

chunks = chunker(l1, 2)
chunk = chunk_with_value(chunks, 1111)
print(chunk)  # [1000, 5000]
print(chunk_to_letter(l1, v2, chunk))  # b
``````

#### @Saleem Ali 2019-10-09 14:26:52

Though other answer is useful but almost all of them is finding interval not desire output from `v2`

``````l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
def get_interval(l1):
for index, left, right in zip(range(len(l1)), l1, l1[1:]):
if left <= element <= right:
return v2[index]

``````

#### @Paul M. 2019-10-09 14:17:10

Straight from the itertools recipe book:

``````def get_thing(value):

def pairwise(iterable):
from itertools import tee
a, b = tee(iterable)
next(b, None)
return zip(a, b)

interval_ranges = [
0,
100,
500,
1000
]

# There are four interval ranges, so three intervals.
things = [
"A", # 0-100
"B", # 100-500
"C" # 500-1000
]

for (begin, end), thing in zip(pairwise(interval_ranges), things):
if begin <= value < end: # modify this to suit your needs. Is the range inclusive/exclusive?
return thing
return None

def main():

thing = get_thing(400)
print(thing)

return 0

if __name__ == "__main__":
import sys
sys.exit(main())
``````

Output:

``````B
``````

#### @rdas 2019-10-09 14:20:24

I believe you need to get the corresponding interval index and use that to query `v2`. This should do it:

``````l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
v2 = ['a','b','c','d','e','f','g'] #7 letters

def intervals(l):
for i in range(len(l)-1):
yield i, l[i:i+2]

def interval_value(val, interval_list, value_list):
for i, interval in intervals(interval_list):
if interval[0] <= val <= interval[1]:
return value_list[i]

print(interval_value(1111, l1, v2))
print(interval_value(0, l1, v2))
print(interval_value(51000, l1, v2))
print(interval_value(40000, l1, v2))
``````

Output:

``````b
a
None
f
``````

You don't need the `v1` values - you can work with the index on `v2` directly

### [SOLVED] How do I check if a list is empty?

• 2008-09-10 06:20:11
• Ray
• 2846825 View
• 3234 Score
• Tags:   python list

### [SOLVED] How do I get the number of elements in a list?

• 2009-11-11 00:30:54
• y2k
• 3258758 View
• 1934 Score
• Tags:   python list

### [SOLVED] Finding the index of an item in a list

• 2008-10-07 01:39:38
• Eugene M
• 3867341 View
• 3163 Score
• Tags:   python list indexing

### [SOLVED] Getting the last element of a list

• 2009-05-30 19:28:53
• Janusz
• 2103531 View
• 2058 Score
• Tags:   python list indexing