2

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))
6
  • The only change I'd personally do would be to create a dict with name as the key. It doesn't change the fact you've still got to iterate over it. Also don't set variable names as min and max because you're overriding the functions. Commented Sep 9, 2021 at 9:32
  • What if more than one person fit the min/max size criteria? Commented Sep 9, 2021 at 9:42
  • Is my_list already sorted like that? Or are there cases when it's not sorted? Commented Sep 9, 2021 at 10:17
  • @Peter, This what you are suggesting "bob01": {"min_size": 0, "max_size": 100, "capacity": 200} ? and yes, min, max is bad to use variables Commented Sep 9, 2021 at 10:35
  • 1
    @timgeb, first matching person is enough, this design actually should fir same values, everyboady has unique capabilities Commented Sep 9, 2021 at 10:38

4 Answers 4

3

Your code is already considerably efficient. Just I would suggest to remove the try except block for more efficiency:

def get_name(n):
    for person in my_list:
        if isinstance(person['min_size'], int) and isinstance(person['max_size'], int):
            if person['min_size'] <= n <= person['max_size']:
                return person['name']
        else:
            return person['name']
Sign up to request clarification or add additional context in comments.

Comments

2

Using dataclass and type hints makes the code much more readable and easier to maintain

import random
from dataclasses import dataclass
from typing import Optional


@dataclass
class Person:
    name: str
    min_size: int
    max_size: int
    capacity: int


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}
]
persons = [Person(**entry) for entry in my_list if
           isinstance(entry['min_size'], int) and isinstance(entry['max_size'], int)]


def get_name(load: int) -> Optional[str]:
    person: Person
    for person in persons:
        if person.min_size <= load <= person.max_size:
            return person.name
    return None


for i in range(10):
    print(f'------------{i}----------------')
    load = random.randint(0, 170000)
    print(f"My load is {load}")
    name = (get_name(load))
    if name is not None:
        print(f"Mr {name} can handle this load")
    else:
        print("No one can handle this load")

Comments

1

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

4 Comments

what if last value like this, {"name": "bob08", "min_size": 150001, "max_size": "any", "capacity": 1} or {"name": "bob08", "min_size": 150001, "max_size": "17000+", "capacity": 1}, instead of "min_size": "any", sorry my input not covered in question, just while testing now, I am thinking to support this kind of inputs
I believe what you mean by "max_size": "17000+" is "max_size": 17000 right? As having the + sign would make it 150001-17000+ which seems odd as a range as it is equivalent to 150001-infinity.
its typo, its actually, {"name": "bob08", "min_size": 150001, "max_size": "170000+",
@Vidya I updated the answer to also accept "any" for the max_size part. You can check it if it fits your requirements :)
1

If you create a Person class, then you can implement custom behaviour to do checks. Here's an example of how you'd do it:

import random

class Person(object):
    def __init__(self, name, min_size, max_size, capacity):
        self.name = name
        self.min_size = min_size
        self.max_size = max_size
        self.capacity = capacity

    def __str__(self):
        return self.name

    def can_carry_load(self, n):
        if self.min_size is None:
            if self.max_size is None:
                return True
            return n <= self.max_size
        if self.max_size is None:
            return self.min_size <= n
        return self.min_size <= n <= self.max_size


my_list = [
    Person("bob01", min_size=0, max_size=100, capacity=200),
    Person("bob02", min_size=101, max_size=1000, capacity=100),
    Person("bob03", min_size=1001, max_size=5000, capacity=50),
    Person("bob04", min_size=5001, max_size=10000, capacity=25),
    Person("bob05", min_size=10001, max_size=50000, capacity=12),
    Person("bob06", min_size=50001, max_size=100000, capacity=6),
    Person("bob07", min_size=100001, max_size=150000, capacity=3),
    Person("bob08", min_size=None, max_size=None, capacity=1),
]

def get_name(n):
    for person in my_list:
        if person.can_carry_load(n):
            return str(person)


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

(You may possibly be able to mix this answer with the dataclass one, it's just not something I have any experience with)

I changed "any" to None, because checking for None is a lot more pythonic than catching the TypeError when you attempt to compare strings to integers.

I also switched to using f-strings as they are useful things worth knowing about, assuming you're on a recent version of Python.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.