Home > Software design >  Python: Trouble with looping nested dictionary
Python: Trouble with looping nested dictionary

Time:09-17

I want to loop through keys in a dictionary, and then see if a nested dictionary has a certain value.

However, Im struggling a bit witht he syntax. This is what I've written:

dict = {
    "Bull's eye": {"Points": 50, "Range": 60},
    "Bull": {"Points": 25, "Range": range(55, 59)},
    "Outer value": {"Points": range(0, 20), "Range": range(1, 54)},
}
number = 2
for key in dict.keys():
    if number in dict[key]["Range"]:
        print(f"Value is in {key}")

Error: "TypeError: argument of type 'int' is not iterable"

Notice that the "number" value is in the "Range" dictionary inside "Outer values". So when the loop finds this value inside the nested dictionary, I want the if-statement to be True

CodePudding user response:

This is because of the first dictionary you have "Bull's eye": {"Points": 50, "Range": 60}. Here, the key is just an int so you cannot use the in keyword because an int isn't iterable. If you're using that syntax the dictionary key needs to be an iterable type such as

dict = {
    "Bull's eye": {"Points": 50, "Range": (60,)},
    "Bull": {"Points": 25, "Range": range(55, 59)},
    "Outer value": {"Points": range(0, 20), "Range": range(1, 54)},
}

CodePudding user response:

The issue is that you have different data types for the Range field, but the in operator assumes an iterable (as produced by the range function). You have several options, as pointed out by BTables, you can make sure that your dict only contains iterables - alternatively, you can check for the data type in your loop.

for key, value in dict_.items():
    range_ = value["Range"]
    if isinstance(range_, range) and number in range_:
        print(f"Value is in {key}")
    elif isinstance(range_, int) and number == range_:
        print(f"Value equals {key}")

Note that I also adjusted the code to use some best practices, such as using a trailing underscore (_) in case the name is already a built-in function or reserved keyword (i.e., I renamed your dict variable to dict_). Also, you can directly loop over the dict's items, instead of only iterating over the keys which made you need to index the dict in the loop.

CodePudding user response:

In addition to @BTables answer, if you're curious the reason why the error said that is not iterable of all things, is because python have several way to solve an a in B instruction, first it check if the special method __contains__ is present in B, and if isn't it will try to iterate over B (aka if it have the special method __iter__) and and do a linear search to find the element

>>> class A:
        def __iter__(self):
            yield from range(10)

        
>>> a=A()
>>> a
<__main__.A object at 0x0000027DC8646CD0>
>>> list(a)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> 1 in a
True
>>> class B:
        def __iter__(self):
            yield from range(10)
        def __contains__(self, item):
            return False

    
>>> b=B()
>>> 1 in b
False
>>>

and the int class have neither of those, hence the error

CodePudding user response:

I suggest you try this example: (should work as is)

dict = {
    "Bull's eye": {"Points": 50, "Range": [60]},
    "Bull": {"Points": 25, "Range": range(55, 59)},
    "Outer value": {"Points": range(0, 20), "Range": range(1, 54)},
}
number = 2
[print(key) for key in dict.keys() if number in dict.get(key).get('Range')]

CodePudding user response:

Your code is raising TypeError due to how you are checking the values. Some of the values are integers while others are of the type Range. You can check if 2 is in a range, but you can't check if 2 is in an integer.

You can use a try/except block to remedy this:

my_dict = {
    "Bull's eye": {"Points": 50, "Range": 60},
    "Bull": {"Points": 25, "Range": range(55, 59)},
    "Outer value": {"Points": range(0, 20), "Range": range(1, 54)},
}
number = 2   
for key, sub_dict in my_dict.items():
    try:
        if number in sub_dict["Range"]:
            print(f"number '{number}' is in key '{key}'")
    except TypeError:
        if number == sub_dict["Range"]:
            print(f"number '{number}' is in key '{key}'")
  • Related