DenaliHardtail
DenaliHardtail

Reputation: 28336

How can I lookup based on multiple parameters?

I have a dictionary like this:

dict = {
        "AA": {"Rad": 1000.0, "Code": "C01"},
        "BB": {"Rad": 2200.0, "Code": "C02"},
        "CC": {"Rad": 2300.0, "Code": "C02"},
        "DD": {"Rad": 3000.0, "Code": "C04"}
    }

Most of the time, selecting the appropriate value is quite simple. My code grabs the value from the dictionary entry by passing in the key and the property I need. For example, if my value is "CC", I get the code with dict["CC"]["Code"]. Simple enough and works the vast majority of the time.

Now, I need to adjust for a change in the business logic. If I follow what I've done in the past, the dictionary will look like this:

dict = {
        "AA": {"Rad": 1000.0, "Code": "C01"},
        "BB": {"Rad": 2200.0, "Code": "C02"},
        "CC": {"Rad": 2300.0, "Code": "C02"},
        "DD": {"Rad": 3000.0, "Code": "C04"},
        "DD": {"Rad": 7000.0, "Code": "C03"}
    }

But this doesn't work. Two identical keys in a dictionary doesn't work. This is problem one.

The other issue, I need to take into account the value of Rad when selecting a Code. Rad is a minimum value used for comparison. If the value in my variable is greater than Rad use corresponding the Code value.

How do I restructure this to accommodate value retrieval by dictionary key (the majority of what this code does) and retrieval by key & Rad value? If necessary, I can through out this dictionary structure and do something different.

Update 2: Refining the business logic a bit. The rad values specified in the dictionary are minimum values. I need to grab the smallest value that is greater than the minimum value regardless of the order in which they are listed in the dictionary.

In the following case,

"DD": [{"Rad": 3000.0, "Code": "C04"},
       {"Rad": 7000.0, "Code": "C03"},
       {"Rad": 4000.0, "Code": "C09"}]]

get(d, "DD", 5000) returns C04 but C09 is expected.

Update 1: A database of any kind is way overkill for this project. This dictionary will, at most, have six entries.

Here is the minimal code to demonstrate what I'm trying to do.

dict = {
        "AA": {"Rad": 1000.0, "Code": "C01"},
        "BB": {"Rad": 2200.0, "Code": "C02"},
        "CC": {"Rad": 2300.0, "Code": "C02"},
        "DD": {"Rad": 3000.0, "Code": "C04"},
        "DD": {"Rad": 7000.0, "Code": "C03"}
    }

type = "DD"
rad = 4000

# need to incorporate rad variable
print dict["type"]["Code"]  

# Should print C04

Upvotes: 3

Views: 607

Answers (5)

mitghi
mitghi

Reputation: 919

You can write a custom type class to do that.

import itertools
import bisect

class cdict(dict):
    def __init__(self, data=None):
        dict.__init__(self)
        if data: self.update(data)

    def __call__(self,base,target):
        res = self.get(base) if base else None
        _st = res.items() if isinstance(res,dict) else res
        if isinstance(res,dict):
            if res and target:
                _item = res['Rad']
                if target >= _item: return res['Code']
        elif isinstance(res,list):
            _iter = sorted ( filter (lambda x: not isinstance(x,str),list (itertools.chain.from_iterable(map(lambda x: x.values(),res)))))
            if len(res) and target:
                _filter = filter (lambda x: x <= target,_iter)
                _sorted =  bisect.bisect(_filter,target)
                if len(_filter) and not _sorted: return filter (lambda x: x['Rad'] == _iter [ _sorted ],res)[0]['Code']
                elif len(_filter) and _sorted: return filter (lambda x: x['Rad'] == _iter [ _sorted -1 ],res)[0]['Code']
        return None

x = cdict({
    "AA": {"Rad": 1000.0, "Code": "C01"},
    "BB": {"Rad": 2200.0, "Code": "C02"},
    "CC": {"Rad": 2300.0, "Code": "C02"},
    "DD": [ {"Rad": 7000.0, "Code": "C04"}, {"Rad": 3000.0, "Code": "C03"} ] } )

print x('AA',2000.0)
print x('DD',8000.0)
print x('DD',4000.0)

C01 C04 C03

Upvotes: 0

tobias_k
tobias_k

Reputation: 82929

You could make the values in your dictionary lists, so one key can be mapped to multiple values.

d = {
        "AA": [{"Rad": 1000.0, "Code": "C01"}],
        "BB": [{"Rad": 2200.0, "Code": "C02"}],
        "CC": [{"Rad": 2300.0, "Code": "C02"}],
        "DD": [{"Rad": 3000.0, "Code": "C04"}, {"Rad": 7000.0, "Code": "C03"}]
    }

For getting the code from the list satisfying the rad constraint, you could try this:

def get(d, t, r):
    try:
        return max((x for x in d[t] if x["Rad"] < r), key=lambda x: x["Rad"])["Code"]
    except ValueError:
        return None

Example:

>>> get(d, "DD", 4000)
C04

Upvotes: 1

khol
khol

Reputation: 276

Are the sub-dictionaries always going to have the same two fields? If so, you could place the "Rad" and "Code" fields into a tuple, then put the tuples into a list, like:

dict = {
            "AA": [(1000.0, "C01")]
            ...
            "DD": [(3000.0,"C04"), (7000.0, "C03")]

       }

To access the "Code" field of the first entry for the "DD" field, use:

dict["DD"][0][1]

Upvotes: 1

James
James

Reputation: 1238

One way to do this is to structure your dictionary like this:

dict = {
    "AA": {1000.0: "C01"},
    "BB": {2200.0: "C02"},
    "CC": {2300.0: "C02"},
    "DD": {3000.0: "C04",
           7000.0: "C03"},
}

Then to get a the Code field, you can write dict["AA"][1000.0].

Upvotes: 2

JuanPablo
JuanPablo

Reputation: 24774

dict = {
        ...
        "DD": {4000:{"code":"C04"}, 7000: {"Code": "C03"}}
    }

Upvotes: 0

Related Questions