basic
basic

Reputation: 43

Create nested json object out of a list of dicts dynamically

I want to translate a list of dictionaries into a nested JSON. I want to dynamically populate fields.I want to group all the signals for a specific category. I have tried different ways, but can't able generate the exact format required. Need some help for logic to generate the JSON

My Input

signal_list = [
    {'sig_id': 667, 'sig_name': 'RotorSpeed', 'Category': 'Gbox'},
    {'sig_id': 672, 'sig_name': 'GeneratorSpeed', 'Category': 'Genarator'},
    {'sig_id': 673, 'sig_name': 'NacelleSpeed', 'Category': 'Nacelle'},
    {'sig_id': 668, 'sig_name': 'RotorDirection', 'Category': 'Gbox'}
]

Code I have tried:

d = defaultdict(list)
for t in signals:
    field_list = d[t['Category']]
    new_d = {'sig_name': t['sig_name'], 'sig_id': t['sig_id'], 'Category': t['Category']}
    field_list.append(defaultdict(list, new_d))

Current output

[
   {
      "Gbox":[
         {
            "sig_name":"RotorSpeed",
            "sig_id":667
         },
         {
            "sig_name":"RotorDirection",
            "sig_id":668
         }
      ]
   },
   {
      "Genarator":[
         {
            "sig_name":"GeneratorSpeed",
            "sig_id":672
         }
      ]
   },
   {
      "Signals":[
         {
            "sig_name":"NacelleSpeed",
            "SignalID":673
         }
      ]
   }
]

(Required)My output should look as below

[
   {
      "Category":"Gbox",
      "Signals":[
         {
            "sig_name":"RotorSpeed",
            "sig_id":667
         },
         {
            "sig_name":"RotorDirection",
            "sig_id":668
         }
      ]
   },
   {
      "Category":"Genarator",
      "Signals":[
         {
            "sig_name":"GeneratorSpeed",
            "sig_id":672
         }
      ]
   },
   {
      "Category":"Nacelle",
      "Signals":[
         {
            "sig_name":"NacelleSpeed",
            "SignalID":673
         }
      ]
   }
]

Upvotes: 1

Views: 389

Answers (1)

Buckeye14Guy
Buckeye14Guy

Reputation: 851

Please refer to the itertools documentation and specifically the groupby function.

import json
import itertools

dta = signal_list = [
    {'sig_id': 667, 'sig_name': 'RotorSpeed', 'Category': 'Gbox'},
    {'sig_id': 672, 'sig_name': 'GeneratorSpeed', 'Category': 'Genarator'},
    {'sig_id': 673, 'sig_name': 'NacelleSpeed', 'Category': 'Nacelle'},
    {'sig_id': 668, 'sig_name': 'RotorDirection', 'Category': 'Gbox'}
]

# this key is simply to be used on each dictionary to get the 'Category'
key = lambda dct: dct.get('Category')

# group works best with a sorted iterable. So let's sort by "Category' since that's
# your grouping key.
a = itertools.groupby(sorted(dta, key=key), key)

# groupby returns a generator like object that has tuples of (key, value)
# the value is also a generator like object that just has each item from the iterable
# that matches your grouping key. To get all the items we just turn them into a list
b = [{"Category": k, "Signals": list(v)} for k,v in a]

# using json to print it out in a nice format
print(json.dumps(b, indent=1))

Output:

[
 {
  "Category": "Gbox",
  "Signals": [
   {
    "sig_id": 667,
    "sig_name": "RotorSpeed",
    "Category": "Gbox"
   },
   {
    "sig_id": 668,
    "sig_name": "RotorDirection",
    "Category": "Gbox"
   }
  ]
 },
 {
  "Category": "Genarator",
  "Signals": [
   {
    "sig_id": 672,
    "sig_name": "GeneratorSpeed",
    "Category": "Genarator"
   }
  ]
 },
 {
  "Category": "Nacelle",
  "Signals": [
   {
    "sig_id": 673,
    "sig_name": "NacelleSpeed",
    "Category": "Nacelle"
   }
  ]
 }
]

If I understand correctly, you want to remove the Category key from each dictionary in your lists. You could do this. If you wanted to remove multiple keys, just edit that function and do some sort of loop inside :)

def remove_key(dct:dict, key):
    """
    Simply takes a dictionary, copies it and remove the key of interest
    from the copy and returns the copy.
    """
    # This function will be used in a list comprehension so it has to
    # return the dictionary and not just pop the key

    dct_ = dct.copy()
    _ = dct_.pop(key)

    return dct_

b = [{"Category": k, "Signals": [remove_key(dct, 'Category') for dct in v]} for k,v in a]

print(json.dumps(b, indent=1))

Output

[
 {
  "Category": "Gbox",
  "Signals": [
   {
    "sig_id": 667,
    "sig_name": "RotorSpeed"
   },
   {
    "sig_id": 668,
    "sig_name": "RotorDirection"
   }
  ]
 },
 {
  "Category": "Genarator",
  "Signals": [
   {
    "sig_id": 672,
    "sig_name": "GeneratorSpeed"
   }
  ]
 },
 {
  "Category": "Nacelle",
  "Signals": [
   {
    "sig_id": 673,
    "sig_name": "NacelleSpeed"
   }
  ]
 }
]

Upvotes: 1

Related Questions