Concat the values into a list to a key in python

Viewed 63

Help me shorten the code and also print the exact same output, as the keys and values may vary every time it runs.
This is the input dictionary:

personal_info_list = [
    {"id": 1, "name": "John", "age": 32, "cars": "BMW"},
    {"id": 1, "title": "engineer", "cars": "Mercedes", "phone": "samsung"},
    {"id": 1, "cars": "Jaguar", "phone": "iphone"},
    {"id": 2, "name": "Charlie", "age": 22, "phone": "iphone"},
    {"id": 2, "title": "doctor", "cars": "BMW", "phone": "samsung"},
    {"id": 3, "name": "Michael", "cars": "Volkswagen", "phone": "nokia"},
    {"id": 3, "title": "doctor", "cars": "BMW", "phone": "samsung"},
]

The output dictionary should be like:

{
 1: {'name': 'John', 'age': 32, 'cars': ['BMW', 'Mercedes', 'Jaguar'], 'title': 'engineer', 'phone': ['samsung', 'iphone']},
 2: {'name': 'Charlie', 'age': 22, 'phone': ['iphone', 'samsung'], 'title': 'doctor', 'cars': 'BMW'},
 3: {'name': 'Michael', 'cars': ['Volkswagen', 'BMW'], 'phone': ['nokia', 'samsung'], 'title': 'doctor'}
}

I tried the code to get the all keys of Ids and Merge it with the rest of the dicts:

def formatDictionary(lst_1):
    dict1={}
    for i in lst_1:
        for j in lst_1:
            temp=[]
            if i['id'] == j['id']:
                for k,v in i.items():
                    S = Merge(i,j)
            dict1[i['id']] = S 
    print(dict1)

formatDictionary(info_list)

Which gave me the output as:

{
1: {'id': 1, 'cars': 'Jaguar', 'phone': 'iphone'}, 
2: {'id': 2, 'title': 'doctor', 'cars': 'BMW', 'phone': 'samsung'}, 
3: {'id': 3, 'title': 'doctor', 'cars': 'BMW', 'phone': 'samsung'}
}
3 Answers

Try using itertools.groupby to get all dictionaries with the same id:

from itertools import groupby
from operator import itemgetter

result = {}
for personal_id, group in groupby(personal_info_list, itemgetter('id')):
    result[personal_id] = {'id': personal_id}
    for dct in group:
        for k, v in dct.items():
            if k != 'id':
                if k in result[personal_id]:
                    if not isinstance(result[personal_id][k], list):
                        result[personal_id][k] = [result[personal_id][k]]
                    result[personal_id][k].append(v)
                else:
                    result[personal_id][k] = v

Here is what result looks like:

{1: {'age': 32,
     'cars': ['BMW', 'Mercedes', 'Jaguar'],
     'id': 1,
     'name': 'John',
     'phone': ['samsung', 'iphone'],
     'title': 'engineer'},
 2: {'age': 22,
     'cars': 'BMW',
     'id': 2,
     'name': 'Charlie',
     'phone': ['iphone', 'samsung'],
     'title': 'doctor'},
 3: {'cars': ['Volkswagen', 'BMW'],
     'id': 3,
     'name': 'Michael',
     'phone': ['nokia', 'samsung'],
     'title': 'doctor'}}

Here is an alternative implementation.

#take the possible IDs
ids = set([d['id'] for d in personal_info_list])

r = {}
for _id in ids: #loop over the IDs and build a new dict
    for d in personal_info_list:
        if d['id'] != _id: #skip if not of interest
            continue
        if _id not in r: #init a sub-dict
            r[_id] = {}
            
        for k,v in d.items(): #populate the dict
            if k in r[_id]:
                if type(r[_id][k]) is str: #make list of values if needed
                    r[_id][k] = [r[_id][k]]
                
                if type(r[_id][k]) is list:
                    r[_id][k].append(v)
            else:
                r[_id][k] = v

r will be

{1: {'id': 1,
  'name': 'John',
  'age': 32,
  'cars': ['BMW', 'Mercedes', 'Jaguar'],
  'title': 'engineer',
  'phone': ['samsung', 'iphone']},
 2: {'id': 2,
  'name': 'Charlie',
  'age': 22,
  'phone': ['iphone', 'samsung'],
  'title': 'doctor',
  'cars': 'BMW'},
 3: {'id': 3,
  'name': 'Michael',
  'cars': ['Volkswagen', 'BMW'],
  'phone': ['nokia', 'samsung'],
  'title': 'doctor'}}

I'm not sure is this solution explicit but for the given example it works as expected:

import pandas as pd

res = (pd.DataFrame(personal_info_list).
       set_index('id').
       groupby('id').
       apply(lambda df: df.dropna(how='all',axis=1).T.apply(lambda x: x.dropna().tolist(),1).
             map(lambda y: y[0] if len(y)==1 else y).to_dict()).
       to_dict())

>>> res
'''
{1: {'name': 'John',
  'age': 32.0,
  'cars': ['BMW', 'Mercedes', 'Jaguar'],
  'title': 'engineer',
  'phone': ['samsung', 'iphone']},
 2: {'name': 'Charlie',
  'age': 22.0,
  'cars': 'BMW',
  'title': 'doctor',
  'phone': ['iphone', 'samsung']},
 3: {'name': 'Michael',
  'cars': ['Volkswagen', 'BMW'],
  'title': 'doctor',
  'phone': ['nokia', 'samsung']}}
Related