Python Dictionary Additions

Question:

I’m a little new to Python and have been able to build a pretty nice application over here, but I’ve been spinning my wheels for a few hours here and I wanted to ask for a little help. Here’s my loop where I’m adding to a dictionary:

for toast in checkin_toasts:
    if toast['checkin_id'] not in toasts:
        toasts[toast['checkin_id']] = {
            'user_id': toast['user_id'],
            'username': toast['username']
        }
    else:
        toasts[toast['checkin_id']] = toasts[toast['checkin_id']],{
            'user_id': toast['user_id'],
            'username': toast['username']
        }

In my case, I currently have a total of three toasts for two different checkins. The above loop gives me this dictionary:

{8: ({'user_id': 1, 'username': 'shaunwo'}, {'user_id': 2, 'username': 'shaunwo2'}), 9: {'user_id': 1, 'username': 'shaunwo'}}

And then when I try to use it in my jinja form, it works as I would expect for checkin 8 with the two toasts, but it does not work for my checkin 9 that only has one toast. I found that if I manually tweak that dictionary a bit to this:

{8: ({'user_id': 1, 'username': 'shaunwo'}, {'user_id': 2, 'username': 'shaunwo2'}), 9: ({'user_id': 1, 'username': 'shaunwo'},)}

THAT works in my jinja form beautifully. But I don’t know how to revise my for loop above accordingly to give me that kind of dictionary with the (,) around the single instance for checkin 9.

Asked By: Shaun Worcester

||

Answers:

You can use the dict.setdefault method to initialize a non-existent key of a dict with a list so that it can be consistently appended with the desired sub-dict:

for toast in checkin_toasts:
    toasts.setdefault(toast['checkin_id'], []).append({
        'user_id': toast['user_id'],
        'username': toast['username']
    })
Answered By: blhsing

The problem is that in one branch of the if statement, you set toasts[toast['checkin_id']] to a dictionary, while in the other you set it to a tuple of its previous value and a dictionary:

        toasts[toast['checkin_id']] = {
            'user_id': toast['user_id'],
            'username': toast['username']
        }

vs.

        toasts[toast['checkin_id']] = (
            toasts[toast['checkin_id']],
            {
                'user_id': toast['user_id'],
                'username': toast['username']
            },
        )

Meanwhile, your frontend is expecting a tuple (or list) of dictionaries, regardless of whether there’s 1, 2 or more.

As @blhsing posted, you can achieve this by always making it a list and appending entries to it; that’s probably the neatest solution.

However, to answer the question as asked, to make a tuple of one item, there’s special syntax for that in Python; a trailing comma:

        toasts[toast['checkin_id']] = {
            'user_id': toast['user_id'],
            'username': toast['username']
        },

If you prefer to make it more explicit, you can add parentheses:

        toasts[toast['checkin_id']] = ({
            'user_id': toast['user_id'],
            'username': toast['username']
        },)``

At that point, you’d also need to fix the other case to make a longer tuple rather than nesting it:

        toasts[toast['checkin_id']] = toasts[toast['checkin_id']] + ({
            'user_id': toast['user_id'],
            'username': toast['username']
        },)

As in @blhsing’s answer, you can also combine those two into one, using the .get() method with a default to give you an empty tuple when adding the first toast:

        toasts[toast['checkin_id']] = toasts.get(toast['checkin_id'], ()) + ({
            'user_id': toast['user_id'],
            'username': toast['username']
        },)
Answered By: Jiří Baum

It seems that you are aggregating the toasts into a tuple if they have the same checkin_id and return a dictionary with checkin_ids as keys and toast tuples as values.

In this case, you could use collections.defaultdict and aggregate the toasts with the same checkin_id with a tuple.

from collections import defaultdict

toasts = defaultdict(tuple)
for toast in checkin_toasts:
    toasts[toast['checkin_id']] += ({
        'user_id': toast['user_id'],
        'username': toast['username']
    }, )

Otherwise, you could recreate the tuple every time by yourself without the help of collections.defaultdict.
This key point is to use the asterisk operator * to unpack the existing tuple and append the new element for the tuple assignment.

for toast in checkin_toasts:
    if toast['checkin_id'] not in toasts:
        # Always create a new key with an empty tuple as its value if the key does not exist.
        toasts[toast['checkin_id']] = tuple()
    
    toasts[toast['checkin_id']] = *toasts[toast['checkin_id']], {      
        'user_id': toast['user_id'],
        'username': toast['username']
    }
    
    # # Equals to 
    # toasts[toast['checkin_id']] = tuple(*toasts[toast['checkin_id']], {      
    #    'user_id': toast['user_id'],
    #    'username': toast['username']
    # })

I would suggest the first approach.

Answered By: jadore801120

Thank you SO MUCH!! I’m back in business. Thank you for the insights.

Answered By: Shaun Worcester
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.