Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: How to get the closest key from the given key?

Given a dictionary:

sample = {
    '123': 'Foo', 
    '456': 'Bar', 
    '789': 'Hello', 
    '-111': 'World'
}

What is the most efficient way (approach and/or data structure) to get the closest (or less) key from a dictionary?

Note:
1. Even if key is a string, comparison should be numerical.
2. Keys can be "negative".

Example:

get_nearest_less_element(sample, '456') # returns 'Bar'
get_nearest_less_element(sample, '235') # returns 'Foo'
get_nearest_less_element(sample, '455') # returns 'Foo'
get_nearest_less_element(sample, '999') # returns 'Hello'
get_nearest_less_element(sample, '0') # returns 'World'
get_nearest_less_element(sample, '-110') # returns 'World'
get_nearest_less_element(sample, '-999') # should return an error since it is beyond the lower bound

Additional question:
Given the same data set, would a sorted OrderedDict or a List of Tuples or any other python data structure be a better approach?

like image 657
Mico Avatar asked Jun 16 '16 06:06

Mico


People also ask

How do I find the closest value in a list Python?

We can find the nearest value in the list by using the min() function. Define a function that calculates the difference between a value in the list and the given value and returns the absolute value of the result. Then call the min() function which returns the closest value to the given value.

How do you compare keys in Python?

The compare method cmp() is used in Python to compare values and keys of two dictionaries. If method returns 0 if both dictionaries are equal, 1 if dic1 > dict2 and -1 if dict1 < dict2.

What does key () do in Python?

The keys() method in Python Dictionary, returns a view object that displays a list of all the keys in the dictionary in order of insertion using Python. Parameters: There are no parameters. Returns: A view object is returned that displays all the keys.


2 Answers

def get_nearest_less_element(d, k):
    k = int(k)
    return d[str(max(key for key in map(int, d.keys()) if key <= k))]

Edit to update with @Paul Hankin's code, but using <= I'm not sure it needs a branch at all. Convert all the keys to numbers, find the ones less than or equal to k, get the max one - if k is in there you'll get it, otherwise you'll get the next biggest - convert back to string and lookup in the dictionary.

Tests: https://repl.it/C2dN/0

I don't know if it's the most efficient idea; since the dictionary you get is unordered, you have to iterate over each element because any of them could be the next biggest, and since you need a numeric comparison you have to convert them all to integers. It seems to me that any other structure would take more initialisation cost as you'd have to go over every item to put it into your structure first.

But it depends on your use case - if k is very likely to be in the dictionary, it makes sense to change my code to have if k in d: return d[k] else: ... branch because not doing the generator expression in that case would be quicker. If it's very likely not in the dictionary, it won't help much.

A pseudocode (untested) version which does sort they keys is below - this would be slower to use once, but possibly faster to query a lot:

# cache to store sorted keys between function calls
# nb. you will have to invalidate this cache (reset to []) 
# when you get a new dictionary
sorted_keys = []

def get_nearest_less_element(d, k):
    if k in d:               # quick return if key is in dict
        return d[k]
    else:

        # costly sort of the keys, only do this once
        if not sorted_keys:
            sorted_keys = sorted(int(key) for key in d.keys())

        # quick run through the sorted key list up
        # to latest item less than k
        k = int(k)
        nearest = sorted_keys[0]
        for item in sorted_keys:
            if item < k:
                nearest = item
            else:
                break

         return d[str(item)]
like image 77
TessellatingHeckler Avatar answered Nov 15 '22 05:11

TessellatingHeckler


The below module returns the value if the key is present else it finds the maximum key in a list of keys that are less than the input key.

def get_nearest_less_element(sample,key):
  if key in sample:
    return sample[key]
  else:
    return sample[str(max(x for x in sample.keys() if int(x) < int(key)))]

print get_nearest_less_element(sample, '456')
print get_nearest_less_element(sample, '235')
print get_nearest_less_element(sample, '455')
print get_nearest_less_element(sample, '999')

Output:

Bar

Foo

Foo

Hello

Edit: Edited the answer based on Paul's comment.

like image 37
gaganso Avatar answered Nov 15 '22 05:11

gaganso