2

I have a list like this:

list = [{'item1': value1,
         'item2': [{'tinyitem21': tinyvalue21, 'tinyitem22': tinyvalue22}]},
        {'item3': [{'tinyitem31': tinyvalue31, 'tinyitem32': tinyvalue32}],
         'item4': value4}]

I want to have a list like this:

list = [{'item1': value1,
         'item2': {'tinyitem21': tinyvalue21, 'tinyitem22': tinyvalue22}},
        {'item3': {'tinyitem31': tinyvalue31, 'tinyitem32': tinyvalue32},
         'item4': value4}]

I have written the code below to try to solve this problem but it doesn't work:

for item in list:
    for smallitem, smallvalue in item:
        if type(smallvalue)=='list':
            new_dict= {}
            for tinyitem in smallvalue:
                name = tinyitem.pop('name')
                new_dict[name] = tinyitem
            smallvalue=new_dict

Can anyone help me?

CDJB
  • 14,043
  • 5
  • 29
  • 55
Liselotte
  • 382
  • 1
  • 8

3 Answers3

6

You can use this function (note that the list is modified inplace):

Code:

def f(l):
    for d in l:
        for k, v in d.items():
            if isinstance(v, list):
                d[k] = v[0]
    return l

Output:

>>> f(lst)
[{'item1': value1,
  'item2': {'tinyitem21': tinyvalue21, 'tinyitem22': tinyvalue22}},
 {'item3': {'tinyitem31': tinyvalue31, 'tinyitem32': tinyvalue32},
  'item4': value4}]

You could also use a list/dict comprehension:

>>> [{k: v[0] if isinstance(v, list) else v for k, v in d.items()} for d in lst]
[{'item1': value1,
  'item2': {'tinyitem21': tinyvalue21, 'tinyitem22': tinyvalue22}},
 {'item3': {'tinyitem31': tinyvalue31, 'tinyitem32': tinyvalue32},
  'item4': value4}]

If you have more nested lists/dicts than one level, you could use this recursive function:

def f(lst):
    return [recursive_function(d) for d in lst] 

def recursive_function(d):
    rv = {}
    for k, v in d.items():
        if isinstance(v, list):
            v = v[0]
            if isinstance(v, dict):
                v = recursive_function(v)
        rv[k] = v
    return rv

Usage:

>>> lst = [{'item1': value1,
            'item2': [{'tinyitem21': [{'tinyitem21': tinyvalue21,
                                       'tinyitem22': tinyvalue22}],
                       'tinyitem22': tinyvalue22}]},
           {'item3': [{'tinyitem31': tinyvalue31, 'tinyitem32': tinyvalue32}],
            'item4': value4}]
>>> f(lst)
[{'item1': 'value1',
  'item2': {'tinyitem21': {'tinyitem21': tinyvalue21,
                           'tinyitem22': tinyvalue22},
            'tinyitem22': tinyvalue22}},
 {'item3': {'tinyitem31': tinyvalue31, 'tinyitem32': tinyvalue32},
  'item4': value'}]
CDJB
  • 14,043
  • 5
  • 29
  • 55
  • The function gives `TypeError: '_io.TextIOWrapper' object is not callable` the second time I use. Could you please let me know why is that the case. Can the function be used only once in a script? – Karthik S Dec 08 '22 at 06:01
1

Here is the solution for first level nested conversion.

import json

input_list = [{'item1':"value",'item2':[{'tinyitem21':'tinyvalue21','tinyitem22':'tinyvalue22'}]},{'item3':[{'tinyitem31':'tinyvalue31','tinyitem32':'tinyvalue32'}],'item4':'value4'}]

for index, j in enumerate(range(len(input_list))):
    for key, value in input_list[index].items():
        if isinstance(value, list) and len(value) == 1:  # Checking for single dictionary as multiple values of dict cannot considered as json
            input_list[index][key] = value[0]

print(json.dumps(input_list, indent=3))

Output:

[
   {
      "item1": "value",
      "item2": {
         "tinyitem21": "tinyvalue21",
         "tinyitem22": "tinyvalue22"
      }
   },
   {
      "item3": {
         "tinyitem31": "tinyvalue31",
         "tinyitem32": "tinyvalue32"
      },
      "item4": "value4"
   }
]

And let say if the value at item2 at index 0 has more than one dict then it should be considered as string value as shown in below output

import json

input_list = [{'item1':"value",'item2':[{'tinyitem21':'tinyvalue21','tinyitem22':'tinyvalue22'},{'tinyitem23':'tinyvalue21','tinyitem24':'tinyvalue22'}]},{'item3':[{'tinyitem31':'tinyvalue31','tinyitem32':'tinyvalue32'}],'item4':'value4'}]


for index, j in enumerate(range(len(input_list))):
    for key, value in input_list[index].items():
        if isinstance(value, list):  # without checking len of list
            input_list[index][key] = ','.join(map(str, value))  # string value be assigned instead of dictionary

print(json.dumps(input_list, indent=3))

Output will be string instead of dict but if you notice item3 being single dict of list but still being assinged as str.

[
   {
      "item1": "value",
      "item2": "{'tinyitem21': 'tinyvalue21', 'tinyitem22': 'tinyvalue22'},{'tinyitem23': 'tinyvalue21', 'tinyitem24': 'tinyvalue22'}"
   },
   {
      "item3": "{'tinyitem31': 'tinyvalue31', 'tinyitem32': 'tinyvalue32'}",
      "item4": "value4"
   }
]

To handle both list with one/many dict with more readability.

for index, j in enumerate(range(len(input_list))):
    for key, value in input_list[index].items():
        if isinstance(value, list) and len(value) == 1:  # Checking for single dictionary as multiple values of dict cannot considered as json
            input_list[index][key] = value[0]
        if isinstance(value, list) and len(value) > 1:  # for list of dict with more than one 
            input_list[index][key] = ','.join(map(str, value))  # string value be assigned instead of dictionary

if you want to use comprehensive way mentioned by CDJB you need to add one more condition to it because if in case of list with size more than 1 it will silently ignore second index.

[{k: v[0] if isinstance(v, list) and len(v) == 1 else v for k, v in d.items()} for d in input_list]

Output:

[
   {
      "item1": "value",
      "item2": "{'tinyitem21': 'tinyvalue21', 'tinyitem22': 'tinyvalue22'},{'tinyitem23': 'tinyvalue21', 'tinyitem24': 'tinyvalue22'}"
   },
   {
      "item3": {
         "tinyitem31": "tinyvalue31",
         "tinyitem32": "tinyvalue32"
      },
      "item4": "value4"
   }
]
Shakeel
  • 1,869
  • 15
  • 23
  • CDJB's comprehensive listing code is only snippet I have copied. Rest all is my effort. Though I have copied that snippet, I have updated on top of his code(i.e, added fix ` len(v) == 1`) but still I have made sure to mention his name over there. So lets stop judging and keep coding Abhi. – Shakeel Feb 07 '20 at 15:58
0

One liner in Python:

lt = [{'item1':'value1', 'item2':[{'tinyitem21':'tinyvalue21','tinyitem22':'tinyvalue22'}]}, {'item3':[{'tinyitem31':'tinyvalue31','tinyitem32':'tinyvalue32'}], 'item4': 'value4'}]

li = [{i:(j[0] if isinstance(j, list) and len(j) > 0 else j) for i,j in m.items()} for m in lt]

print(li)

Outputs:

[{'item1': 'value1', 'item2': {'tinyitem21': 'tinyvalue21', 'tinyitem22': 'tinyvalue22'}}, {'item3': {'tinyitem31': 'tinyvalue31', 'tinyitem32': 'tinyvalue32'}, 'item4': 'value4'}]

See this for "How to use if/else in a dictionary comprehension?".

abhiarora
  • 9,743
  • 5
  • 32
  • 57