Tume
Tume

Reputation: 15

How to loop through values in JSON and assign to another dictionary

I am developing a Python/Django web app. I am trying to parse JSON into a python dictionary, read the values in the dictionary, and assign the values to another dictionary if certain conditions are met.

JSON is structured like this:

{content: {cars: [0, 1, 2]}, other_stuff: []}

Each car has multiple attributes:

0: {"make", "model", "power"...}

Each attribute has three variables:

make: {"value": "Toyota", "unit": "", "user_edited": "false"} 

I am trying to assign the values in the JSON to other dictionaries; car_0, car_1 and car_2. In this case the JSON response is otherwise identical considering each car, but the 'make' of the first car is changed to 'Nissan', and I'm trying to then change the make of the car_0 also to 'Nissan'. I'm parsing JSON in the following way:

    local_cars = [car_0, car_1, car_2] # Dictionaries which are already initialized.

    print(local_cars[0] is local_cars[1]) # Prints: false
    print(local_cars[0]['make']['value']) # Prints: Toyota (yes)
    print(local_cars[1]['make']['value']) # Prints: Toyota (yes)
    print(local_cars[2]['make']['value']) # Prints: Toyota (yes)
    
    counter = 0
    
    if request.method == 'POST':
        payload = json.loads(request.body)
        if bool(payload):
            print(len(local_cars)) # Prints: 3
            print(counter, payload['cars'][0]['make']['value']) # Prints: Nissan (yes)
            print(counter, payload['cars'][1]['make']['value']) # Prints: Toyota (yes)
            print(counter, payload['cars'][2]['make']['value']) # Prints: Toyota (yes)
            print(counter, local_cars[0]['make']['value']) # Prints: Toyota (yes)
            print(counter, local_cars[1]['make']['value']) # Prints: Toyota (yes)
            print(counter, local_cars[2]['make']['value']) # Prints: Toyota (yes)
            for target_car in payload['cars']: # Loop through all three cars in payload
                print(local_cars[0] is local_cars[1]) # false
                for attr in target_car.items(): # Loop through all key:dict pairs of a single car
                    attribute_key = attr[0] # Key (eg. 'make')
                    vars_dict = attr[1] # Dictionary of variables ('value': 'xx', 'unit': 'yy', 'user_edited': 'zz')
                    if vars_dict['user_edited'] == 'true':
                        local_cars[counter][attribute_key]['user_edited'] = 'true'
                        local_cars[counter][attribute_key]['value'] = vars_dict['value']
                print(counter, local_cars[counter]['make']['value']) # Prints: 0, Toyota (yes), 1, Nissan (no!), 2, Nissan (no!)              
                counter = counter + 1

What I don't understand is why the other cars, local_cars[1] and local_cars[2] are affected in anyway in this loop. As it can be seen, for some reason their 'make' is changed to 'Nissan' even though it was 'Toyota' in the request body. This seems to happen in the first round of 'for target_car in payload['cars'].

Abandoning the loop/counter and focusing on one car does not make any difference:

for target_car in payload['cars']: --> target_car = payload['cars'][0]: 
...
local_cars[0][attribute_key]['user_edited'] = 'true'
local_cars[0][attribute_key]['value'] = vars_dict['value'] 

What am I doing wrong? How can the car_1 and car_2 be affected even if I change the only part of the code where any values in those dictionaries are edited to affect only on the local_cars[0]?

UPDATED

Received the correct answer for this. Before the part of code originally posted, I initialized the car_0, car_1 and car_2 dictionaries.

What I did before was:

default_car = model_to_dict(Car.objects.first())
    
    car_0 = {}
    car_1 = {}
    car_2 = {}
    
    attribute = {}
    
    i = 0
    for key, value in default_car.items():
        if i > 1:
            attribute[key] = {"value": value, "unit": units.get(key), "user_edited": "false"}
        i = i + 1
        
    car_0.update(attribute)
    car_1.update(attribute)
    car_2.update(attribute)
    
    local_cars = [car_0, car_1, car_2]
    ...

Apparently it was the problem that all car_x had a connection to attribute-dictionary. I solved the problem by editing the car_x initialization to the following:

default_car = model_to_dict(Car.objects.first())
    
    car_0 = {}
    car_1 = {}
    car_2 = {}
    
    attribute_0 = {}
    attribute_1 = {}
    attribute_2 = {}
    
    i = 0
    for key, value in default_car.items():
        if i > 1:
            attribute_0[key] = {"value": value, "unit": units.get(key), "user_edited": "false"}
            attribute_1[key] = {"value": value, "unit": units.get(key), "user_edited": "false"}
            attribute_2[key] = {"value": value, "unit": units.get(key), "user_edited": "false"}
        i = i + 1
        
    car_0.update(attribute_0)
    car_1.update(attribute_1)
    car_2.update(attribute_2)
    
    local_cars = [car_0, car_1, car_2]
    ...

Upvotes: 1

Views: 504

Answers (1)

nigel222
nigel222

Reputation: 8222

I think you are probably failing to take copies of car_0 etc. Don't forget that python assignment is purely name-binding.

x = car_0
y = car_0
print( x['make']['value'] ) # 'Toyota'
print( y['make']['value'] ) # 'Toyota'
print( x is y ) # True. Both names refer to the same object
x['make']['value'] = 'foo'
print( y['make']['value'] ) # 'foo'

Should have been y = car_0.copy() or even y=car_0.deepcopy().

I don't fully follow your code, but if you are still unsure then do some is testing to find out which entities are bound to the same object (and shouldn't be).

Upvotes: 1

Related Questions