Get name based on input from list dictionary python

Based upon input value, I need to get corresponding name. I have my input my_list which contains name, min_size and max_size. If input value is falls under min_size and max_size, we need return corresponding name

Below code is working fine, but can we improve this in terms of performance and readability by adding any mapping functionality?

code

import random
my_list  = [
            {"name": "bob01", "min_size": 0, "max_size": 100, "capacity": 200},
            {"name": "bob02", "min_size": 101, "max_size": 1000, "capacity": 100},
            {"name": "bob03", "min_size": 1001, "max_size": 5000, "capacity": 50},
            {"name": "bob04", "min_size": 5001, "max_size": 10000, "capacity": 25},
            {"name": "bob05", "min_size": 10001, "max_size": 50000, "capacity": 12},
            {"name": "bob06", "min_size": 50001, "max_size": 100000, "capacity": 6},
            {"name": "bob07", "min_size": 100001, "max_size": 150000, "capacity": 3},
            {"name": "bob08", "min_size": "any", "max_size": "any", "capacity": 1}
        ]
def get_name(n):
    for person in my_list:
        try:
            min = person['min_size']
            max = person['max_size']
            if min <= n <= max:
                return person['name']
        except TypeError:
            return person['name'] 

for i in range(10):
    n = random.randint(0, 170000)
    print("My load is ", n)
    name = (get_name(n))
    print("Mr {} can handle this load".format(name))

Answer

Note: If my_list is just 8 items, my answer might just be a negligible micro-optimization.

Iterating the whole my_list of length n just to get the corresponding name for every chosen number of length m would result to a time complexity of O(n * m). Thus if my_list is 8 items and we would get the name of 100,000 numbers, this means we would be iterating 800,000 times on worst cases.

An improvement is by storing a sorted list of all min_size. Then, for every chosen number, we don’t need to iterate over the whole my_list, we just need to perform a binary search on the smallest number of min_size possible. This would result to O(nlogn + mlogn) operation. Thus if my_list is 8 items and we would get the name of 100,000 numbers, this means we would be iterating 300,024 times on worst cases. This is at the expense of additional O(n) in space complexity.

import bisect
import random

my_list  = [
    {"name": "bob01-0", "min_size": 0, "max_size": 100, "capacity": 200},
    {"name": "bob02-101", "min_size": 101, "max_size": 1000, "capacity": 100},
    {"name": "bob03-1001", "min_size": 1001, "max_size": 5000, "capacity": 50},
    {"name": "bob04-5001", "min_size": 5001, "max_size": 10000, "capacity": 25},
    {"name": "bob05-10001", "min_size": 10001, "max_size": 50000, "capacity": 12},
    {"name": "bob06-50001", "min_size": 50001, "max_size": 100000, "capacity": 6},
    {"name": "bob07-100001", "min_size": 100001, "max_size": 150000, "capacity": 3},
    {"name": "bob08-last", "min_size": "any", "max_size": "any", "capacity": 1},  # The last
    # {"name": "bob08-last", "min_size": 150001, "max_size": "any", "capacity": 1},  # This could also be the last
    # {"name": "bob08-last", "min_size": 150001, "max_size": "17000+", "capacity": 1},  # This could also be the last
]

# O(n) operation. Setup a hash table so that getting the name based on min_size will just be O(1).
my_dict = {
    data["min_size"]: data
    for data in my_list
}

# O(n log n) operation as we need to sort. Collect all the min_size and sort it.
min_sizes = sorted(
    [data["min_size"] for data in my_list if isinstance(data["min_size"], int)]
)

def get_name(n):
    # O(log n) operation. Perform a binary search on the sorted list to know the corresponding min_size.
    n_index = bisect.bisect_left(min_sizes, n)

    if n_index == len(min_sizes):
        if not isinstance(my_dict[min_sizes[-1]]["max_size"], int) 
                or my_dict[min_sizes[-1]]["max_size"] >= n:
            return my_dict[min_sizes[-1]]["name"]
        else:
            return my_dict["any"]["name"]
    elif min_sizes[n_index] == n:
        return my_dict[min_sizes[n_index]]["name"]
    elif min_sizes[n_index] > n:
        return my_dict[min_sizes[n_index-1]]["name"]

# O(m) operation. Process all input numbers.
for i in range(10):
    n = random.randint(0, 170000)
    print("My load is", n)
    name = (get_name(n))
    print("Mr {} can handle this load".format(name))

Output:

My load is 22288
Mr bob05-10001 can handle this load
My load is 6296
Mr bob04-5001 can handle this load
My load is 153706
Mr bob08-last can handle this load
My load is 72470
Mr bob06-50001 can handle this load
My load is 36776
Mr bob05-10001 can handle this load
My load is 112478
Mr bob07-100001 can handle this load
My load is 67958
Mr bob06-50001 can handle this load
My load is 69366
Mr bob06-50001 can handle this load
My load is 56379
Mr bob06-50001 can handle this load
My load is 119832
Mr bob07-100001 can handle this load