in nested dictionary dynamically test if the value is a dictionary or a dictionary list

Question:

I iterate through a nested dictionary taken from a json including one of the keys ("price") and sometimes a list sometimes a dictionary.

Data={"main": {"sub_main": [   
    
{"id": "995", "item": "850", "price": {"ref": "razorback", "value": "250"}},
    
{"id": "953", "item": "763", "price": [{"ref": "razorback", "value": "450"},{"ref": "sumatra", "value": "370"},{"ref": "ligea", "value": "320"} ]}, 
    
]}}

I filter the result according to the value of another key ("id").

# here, the value of "price" key is a dict
result1 = [item["price"] for item in Data["main"]["sub_main"] if item["id"]=="995"]

# here, the value of "price" key is a list of dict
result2 = [item["price"] for item in Data["main"]["sub_main"] if item["id"]=="953"]

then I convert the result into a dictionary

#here I have what I wanted, because the "price" key is a dict

dresult={k:v for e in result1 for (k,v) in e.items()}

but when the "price" key has a dictionary list as its value, it doesn’t work, because of course I get an error "’list’ object has no attribute ‘items’

#it cannot loop on the value and key because the "price" key is a list

dresult={k:v for e in result2 for (k,v) in e.items()}

how to make it convert the result to dictionary in both cases (because I’m iterating through thousands of data). how to dynamically do a type test and change to finally have a dictionary.I would like to use the result to display in a view in Django. I need it to be a dictionary

thank you.

Asked By: Zembla

||

Answers:

I hope I’ve understood your question right. In this code I distinguish if item['price'] is dict/list and create a new dict from ref/value keys:

Data = {
    "main": {
        "sub_main": [
            {
                "id": "995",
                "item": "850",
                "price": {"ref": "razorback", "value": "250"},
            },
            {
                "id": "953",
                "item": "763",
                "price": [
                    {"ref": "razorback", "value": "450"},
                    {"ref": "sumatra", "value": "370"},
                    {"ref": "ligea", "value": "320"},
                ],
            },
        ]
    }
}


ids = "995", "953"

for id_ in ids:
    out = {
        d["ref"]: d["value"]
        for item in Data["main"]["sub_main"]
        for d in (
            [item["price"]]
            if isinstance(item["price"], dict)
            else item["price"]
        )
        if item["id"] == id_
    }
    print(id_, out)

Prints:

995 {'razorback': '250'}
953 {'razorback': '450', 'sumatra': '370', 'ligea': '320'}
Answered By: Andrej Kesely

so I made a function that you will pass id of the sub_main dict you want and will give you price as a dictionary

def get_result(id_):
    price= [item["price"] for item in Data["main"]["sub_main"] if item["id"]==id_][0]
    match type(price).__name__:
        case 'dict':return [price]
        case 'list':return price

then to get result you pass the id as string

dresult=get_result('953')#[{'ref': 'razorback', 'value': '450'}, {'ref': 'sumatra', 'value': '370'}, {'ref': 'ligea', 'value': '320'}]
dresult=get_result('995')#[{'ref': 'razorback', 'value': '250'}]

how to render in view

in your view you can render the context as

return render(request, 'app_name/template_name.html',{'dictionaries': dresult})

reason behind

you can not a get many dictionaries unwrapped ie.{'ref': 'razorback', 'value': '450'}, {'ref': 'sumatra', 'value': '370'}, {'ref': 'ligea', 'value': '320'}.and since your result seem to have a variable number of dicts so its better to wrap the single dict in a list so that in your template you can use

{% for dictionary in dictionaries %}:
  {{ dictionary.ref}}#to get ref
{{ dictionary.value}}#to get value
        ...
{%endfor%}

edit:

override get_context_data

if its a detail or update view where you pass your pk you can pass the pk to the get_result function like below

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        
        context['dictionaries'] = get_result(self.kwargs.get('pk'))
        return context
Answered By: Dante