user2899444
user2899444

Reputation: 323

Python Dictionaries: Grouping Key, Value pairs based on a common key, value

I have a list that contains dictionaries like this:

list1 = [{'name': 'bob', 'email': '[email protected]', 'address': '123 house lane', 
'student_id': 12345}, {'name': 'steve', 'email': '[email protected]',
'address': '456 house lane', 'student_id': 34567}, {'name': 'bob',
'email': '[email protected]', 'address': '789 house lane', 'student_id': 45678}]

Is there a way in python to group selected key, values pairs within a new dictionary based on 'name' value? For instance, something like this as an end result:

new_list = [
    {'name': 'bob', 
         {'emails': ['[email protected]', 
                    '[email protected]']}, 
         {'address': ['123 house lane', 
                    '789 house lane']},
    {'name': 'steve',
        {'email': ... },
        {'address': ... }}
      # let's assume the list1 has various entries at some point 
      # which may or may not have duplicate 'name' values
      # and new_list will hold the groupings
]

Upvotes: 0

Views: 927

Answers (2)

sumit-sampang-rai
sumit-sampang-rai

Reputation: 691

The code below gives you nested dictionaries. Nested dictionaries give you faster processing to find the key while in list you have to create a loop.

list1 = [{'name': 'bob', 'email': '[email protected]', 'address': '123 house lane', 
'student_id': 12345}, {'name': 'steve', 'email': '[email protected]',
'address': '456 house lane', 'student_id': 34567}, {'name': 'bob',
'email': '[email protected]', 'address': '789 house lane', 'student_id': 45678}]

dict1 = {}
for content in list1:
    if content['name'] in [name for name in dict1]:
        dict1[content['name']] = {'emails': dict1[content['name']]['emails'] + [content['address']], 'addresses': dict1[content['name']]['addresses'] + [content['email']]}
    else:
        dict1[content['name']] = {'emails': [content['email']], 'addresses': [content['address']]}
print dict1

Output of the code is

{'steve': {'emails': ['[email protected]'], 'addresses': ['456 house lane']}, 'bob': {'emails': ['[email protected]', '789 house lane'], 'addresses': ['123 house lane', '[email protected]']}}

Upvotes: 1

Adam Smith
Adam Smith

Reputation: 54163

Sounds like this is what you want to do:

list1 = [{'name': 'bob', 'email': '[email protected]', 
          'address': '123 house lane', 'student_id': 12345},
         {'name': 'steve', 'email': '[email protected]',
          'address': '456 house lane', 'student_id': 34567},
         {'name': 'bob', 'email': '[email protected]',
          'address': '789 house lane', 'student_id': 45678}]

import operator
list1.sort(key=operator.itemgetter('name'))

new_list = []
for studentname, dicts in itertools.groupby(list1, operator.itemgetter('name')):
    d = {'name': studentname}
    for dct in dicts:
        for key,value in dct.items():
            if key == 'name':
                continue
            d.setdefault(key, []).append(value)
    new_list.append(d)

DEMO:

[{'address': ['123 house lane', '789 house lane'],
  'email': ['[email protected]', '[email protected]'],
  'name': 'bob',
  'student_id': [12345, 45678]},
 {'address': ['456 house lane'],
  'email': ['[email protected]'],
  'name': 'steve',
  'student_id': [34567]}]

If you were going to use this extensively you should probably hard-code some better names (addresses instead of address for instance) and make a mapping that populates them for you.

keys_mapping = {'address': 'addresses',
                'email': 'emails',
                'student_id': 'student_ids'}

for studentname, dicts in itertools.groupby(list1, operator.itemgetter('name')):
    d = {'name': studentname}
    for dct in dicts:
        for key,value in dct_items():
            new_key = keys_mapping.get(key,key)
            # get the updated value if it's defined, else give `key`
            d.setdefault(new_key, []).append(value)
    new_list.append(d)

Upvotes: 2

Related Questions