简体   繁体   中英

Find index of the key in a dictionary of list

Given a dictionary of lists

node_to_index = {"global": [0],
                  "l_eye": list(range(42, 48)),
                  "r_eye": list(range(36, 42)),
                  "l_brow": list(range(22, 27)),
                  "r_brow": list(range(17, 22)),
                  "mouth": list(range(48, 68)),
                  "nose": list(range(27, 35)),
                 }

Given a number eg 37, it would return index of the "r_eye" key which is 2

I'd go with a generator expression and enumerate , with next to shortcut as soon as the condition is satisfied:

next((ix for ix, (_,v) in enumerate(node_to_index.items()) if 37 in v), None)
# 2

Note that for python versions under 3.7, dictionaries' insertion order is not preserved. So you should consider using collections.OrderedDict :

from collections import OrderedDict

node_to_index = OrderedDict({
    "global": [0],
    "l_eye": list(range(42, 48)),
    "r_eye": list(range(36, 42)),
    "l_brow": list(range(22, 27)),
    "r_brow": list(range(17, 22)),
    "mouth": list(range(48, 68)),
    "nose": list(range(27, 35)),
})

next((ix for ix, (_,v) in enumerate(node_to_index.items()) if 37 in v), None)
# 2

You should use collections.OrderedDict to guarantee its order(index).

from collections import OrderedDict

node_to_index = OrderedDict({
    "global": [0],
    "l_eye": list(range(42, 48)),
    "r_eye": list(range(36, 42)),
    "l_brow": list(range(22, 27)),
    "r_brow": list(range(17, 22)),
    "mouth": list(range(48, 68)),
    "nose": list(range(27, 35)),
})


def find_index(target: int) -> int:
    for idx, value in enumerate(node_to_index.values()):
        if target in value:
            return idx
    return -1


print(find_index(37))

output:

2

Note

If you are using CPython earlier than 3.6 or other interpreter earlier than 3.7, order of dict isn't guaranteed at even creation. So do this:

node_to_index = OrderedDict([
    ('global', [0]),
    ('l_eye', list(range(42, 48))),
    ('r_eye', list(range(36, 42))),
    ('l_brow', list(range(22, 27))),
    ('r_brow', list(range(17, 22))),
    ('mouth', list(range(48, 68))),
    ('nose', list(range(27, 35))),
])

Policy for Absence

It is not quite relative OP's question, but appended for comments.
You have three options for absence of target value.

return -1
raise ValueError(f'{target} is not found')
return None

Note that if the function returns in some cases, omitting otherwise's return None is not recommended by PEP8.

You just iterate through the items until 37 is in the list associated to that key.

Note that you have to handle edge cases like what to do if several lists contain the number (currently it's the first one that is returned) or if no list contains it (currently it returns None ).

def find_number(number, my_dict):
    for n, (key, value) in enumerate(my_dict.items()):
        if number in value:
            return n

node_to_index = {"global": [0],
                 "l_eye": list(range(42, 48)),
                 "r_eye": list(range(36, 42)),
                 "l_brow": list(range(22, 27)),
                 "r_brow": list(range(17, 22)),
                 "mouth": list(range(48, 68)),
                 "nose": list(range(27, 35)),
                }

find_number(37, node_to_index) # returns 2
node_to_index = {"global": [0],
                 "l_eye": list(range(42, 48)),
                 "r_eye": list(range(36, 42)),
                 "l_brow": list(range(22, 27)),
                 "r_brow": list(range(17, 22)),
                 "mouth": list(range(48, 68)),
                 "nose": list(range(27, 35)),
                 }


def get_key(val):
    pos = 0
    for key, value in node_to_index.items():
         for elem in value:
             if elem == val:
                 return pos
         pos += 1


print(get_key(27))


If you are certain that the value is present in one of the lists, you could use the index() method on a list comprehension:

value = 37
index = [value in nodes for nodes in node_to_index.values()].index(True)

print(index) # 2 

Note that using a dictionary for this is probably not the best choice. You seem to only need a list of tuples. This would make the index more reliable and meaningful:

node_to_index = [ ( "global", [0]),
                  ( "l_eye",  list(range(42, 48) ),
                  ( "r_eye",  list(range(36, 42) ),
                  ( "l_brow", list(range(22, 27) ),
                  ( "r_brow", list(range(17, 22) ),
                  ( "mouth",  list(range(48, 68) ),
                  ( "nose",   list(range(27, 35) )
                ]

value = 37
index = [value in nodes for _,nodes in node_to_index].index(True)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM