1

I am trying to convert all Lists inside dict in dict by considering list index as its key.

Sample Input :

{
    "checksum": "c540fcd985bf88c87e48c2bfa1df5498",
    "data": {
        "sampleMetrics": {
            "name": "DNA Library QC Metrics",
            "passQualityControl": true,
            "metrics": [{
                "name": "CONTAMINATION_SCORE",
                "value": 1302,
                "LSL": 0,
                "USL": 3106,
                "UOM": "NA"
            }]
        }
    }
}

Expected output :

{
    "checksum": "c540fcd985bf88c87e48c2bfa1df5498",
    "data": {
        "sampleMetrics": {
            "name": "DNA Library QC Metrics",
            "passQualityControl": true,
            "metrics": {
                "0": {
                    "name": "CONTAMINATION_SCORE"
                },
                "1": {
                    "value": 1302
                },
                "2": {
                    "LSL": 0
                },
                "3": {
                    "USL": 3106
                },
                "4": {
                    "UOM": "NA"
                }
            }
        }
    }
}

Trial :

def list_to_dict_by_index(lst):
    print  {str(k): str(v) for k, v in enumerate(lst)}

list_to_dict_by_index([ {"d1" : 1}, {"d2" : 2} ])

But this is working for simple list. How can I do the same for all lists in dict?

(No matter wherever list is there in dict.)

List may contain another list:

ex: sample input2:

"metrics": [{
    "name": ["CONTAMINATION_SCORE", "TOTAL_SCORE"],
    "value": 1302,
    "LSL": 0,
    "USL": 3106,
    "UOM": "NA"
}]

sample output2:

"metrics" : {
    "0": {
        "name": {
            "0": "CONTAMINATION_SCORE",
            "1": "TOTAL_SCORE"
        }
    },
    "1": {
        "value": 1302
    },
    "2": {
        "LSL": 0
    },
    "3": {
        "USL": 3106
    },
    "4": {
        "UOM": "NA"
    }
}
Harsha Biyani
  • 7,049
  • 9
  • 37
  • 61
  • As an idea I could recommend to take a look on "dict deep merge" question and answer: https://stackoverflow.com/questions/20656135/python-deep-merge-dictionary-data Looks like you need to change a few lines to get what you want. If there will be any problems with that I could describe changes in more details. – Fedor Dikarev Mar 21 '19 at 06:27
  • I don't want to merge the dict. I just want to convert all lists from dict in dict by considering list index as key. – Harsha Biyani Mar 21 '19 at 06:28
  • Ok. That was just an idea of right solution. So if you take that answer as base, add `elif isinstance(value, list):` and put your code inside than you will get your solution. Should I describe my idea in more details and with more code example? – Fedor Dikarev Mar 21 '19 at 06:33
  • @FedorDikarev : I tried `isinstance(value, list)` already. But not able to get in expected way – Harsha Biyani Mar 21 '19 at 06:34

3 Answers3

0
dic = {
    "checksum": "c540fcd985bf88c87e48c2bfa1df5498",
    "data": {
        "sampleMetrics": {
            "name": "DNA Library QC Metrics",
            "passQualityControl": True,
            "metrics": [{
                "name": "CONTAMINATION_SCORE",
                "value": 1302,
                "LSL": 0,
                "USL": 3106,
                "UOM": "NA"
            }]
        }
    }
}

dic2 = dic['data']['sampleMetrics']['metrics']
dic3 ={}
for i in dic2:
    for index,  j in enumerate(i,0):
        dic3[index]={j:i[j]}

dic['data']['sampleMetrics']['metrics'] = dic3

print(dic)

"""
output 
{
  'checksum': 'c540fcd985bf88c87e48c2bfa1df5498', 
  'data': {
           'sampleMetrics': {
                  'name': 'DNA Library QC Metrics',
                  'passQualityControl': True,
                  'metrics': {
                        0: {
                              'name': 'CONTAMINATION_SCORE'
                            },
                        1: {
                            'value': 1302
                            }, 
                        2: { 
                                'LSL': 0
                           },
                        3: {
                            'USL': 3106
                           },
                        4: {
                              'UOM': 'NA'
                            }
                            }
                           }
        }
}
"""
sahasrara62
  • 10,069
  • 3
  • 29
  • 44
  • `dic['data']['sampleMetrics']['metrics']` is just example mentioned in question. List might come anywhere any is key (/parent keys) might be anything. – Harsha Biyani Mar 21 '19 at 06:22
  • @HarshaBiyani then you need to check that whether it is list or not , if list then check inside it is dict or not . if dict then just use the method i did here – sahasrara62 Mar 21 '19 at 06:24
  • Yes I know this simple answer. And I have tried this with recursive function as well. But could not succeeded . – Harsha Biyani Mar 21 '19 at 06:26
  • better to provide the full input data and ask question accordingly and structure the json so it follow the same structure throughout – sahasrara62 Mar 21 '19 at 06:27
  • `Converting all lists in dict to dict by considering list index as key in python `. This is my question. If you read question again, you will get what I wanted to achieve. – Harsha Biyani Mar 21 '19 at 06:30
0

Your second sample input/ouput contains components that are consistent with the question title, namely, the transformation of lists into dictionaries with list indices as keys:

# input
"name": ["CONTAMINATION_SCORE", "TOTAL_SCORE"]

# output
"name": {
    "0": "CONTAMINATION_SCORE",
    "1": "TOTAL_SCORE"
}

However both sample input/output contain lists of dictionaries, which are expected to be transformed in a different manner, i.e. into a dictionary of dictionaries with keys as the enumerable indices of the dictionary's entries.

# input
"metrics": [{
    ...
    "USL": 3106,
    "UOM": "NA"
}]

# output
"metrics" : {
    ...
    "3": {
        "USL": 3106
    },
    "4": {
        "UOM": "NA"
    }
}

This is a lot of words that attempt to articulate essentially the following two cases:

  1. {[{'foo': 'bar'}]} => {'0': {'foo': 'bar'}}
  2. {'foo': ['bar']} => {'foo': {'0': 'bar'}}

This may be a source of failure for you. Additionally, your attempt at solution only iterates over the top-most level of the dictionary. You must recursively traverse the dictionary if you want to affect entries at arbitrary levels, i.e. you want something of the form:

from collections import abv
def update(d):
    for k, v in d.copy().items():
        if isinstance(v, abc.Mapping):
            d[k] = update(v)
        else:
            d[k] = iv
    return d

Use iteritems instead of items if you're using python 2 rather than python 3. Also, copy is necessary so that the iterator is not invalidated when the dictionary is mutated.

You can work in an enumerative loop like you initially used to get a working solution. Careful to add recursive calls to affect all levels of the dictionary. Collectively this might look something like the following:

from collections import abc

def list_of_dict_to_dict(d):
    dd = {}
    for i, (key, val) in enumerate(d.copy().items()):
        dd[i] = {}
        if isinstance(val, abc.Mapping):
            dd[i][key] = transform_dict(val)
        elif isinstance(val, list):
            dd[i][key] = list_to_dict(val)
        else:
            dd[i][key] = val
    return dd

def list_to_dict(l):
    d = {}
    for i, val in enumerate(l):
        if isinstance(val, abc.Mapping):
            d[i] = transform_dict(val)
        else:
            d[i] = val
    return d

def transform_dict(d):
    for k, v in d.copy().items():
        if isinstance(v, list):
            if isinstance(v[0], abc.Mapping) and len(v) == 1:
                d[k] = list_of_dict_to_dict(v[0])
            else:
                d[k] = list_to_dict(v)
        elif isinstance(v, abc.Mapping):
            d[k] = transform_dict(v)
        else:
            d[k] = v
    return d

This assumes the list of dictionaries case always contains a single dictionary. It isn't clear what you expect in other cases.

pkfm
  • 451
  • 3
  • 7
  • This is giving me output same as input. – Harsha Biyani Mar 21 '19 at 08:41
  • Indeed. As is, this code does not accomplish what you want. It is a basis for a solution that can be derived from hints in the answer. – pkfm Mar 21 '19 at 09:16
  • I knew about `isinstance`. I already tried with `if isinstance(v, list)` but no luck – Harsha Biyani Mar 21 '19 at 10:38
  • You definitely need to use `if is instance(v, list)` to determine if each entry is a list so that you can take the appropriate action. The code I posted will traverse the dictionary, which is a start. – pkfm Mar 21 '19 at 17:49
  • @HarshaBiyani added working code, though its not pretty. – pkfm Mar 22 '19 at 07:00
0

What you are asking is clear, but your first example does not match the rule "Converting all lists in dict to dict by considering list index as key". The metrics key is mapped to a list with one element, and that element is a dictionary: [{...}]. Hence, your expected output is:

...
        "metrics": {
            "0": {
                "name": "CONTAMINATION_SCORE",
                "value": 1302,
                "LSL": 0,
                "USL": 3106,
                "UOM": "NA"
            }
        }
...

If this is what you want, you just have to use a DFS:

def list_to_dict_by_key(json_value):
    if isinstance(json_value, list):
        return {str(i):list_to_dict_by_key(v) for i,v in enumerate(json_value)}
    elif isinstance(json_value, dict):
        return {k:list_to_dict_by_key(v) for k,v in json_value.items()}
    else:
        return json_value

The lists are replaced by dictionaries. The values of the dictionaries are processed.

>>> list_to_dict_by_key(sample1)
{'checksum': 'c540fcd985bf88c87e48c2bfa1df5498', 'data': {'sampleMetrics': {'name': 'DNA Library QC Metrics', 'passQualityControl': True, 'metrics': {'0': {'name': 'CONTAMINATION_SCORE', 'value': 1302, 'LSL': 0, 'USL': 3106, 'UOM': 'NA'}}}}}
>>> list_to_dict_by_key(sample2)
{'checksum': 'c540fcd985bf88c87e48c2bfa1df5498', 'data': {'sampleMetrics': {'name': 'DNA Library QC Metrics', 'passQualityControl': True, 'metrics': {'0': {'name': {'0': 'CONTAMINATION_SCORE', '1': 'TOTAL_SCORE'}, 'value': 1302, 'LSL': 0, 'USL': 3106, 'UOM': 'NA'}}}}}

EDIT: sample1 is your first Sample Input, and sample2 is the almost the same: "name": ["CONTAMINATION_SCORE", "TOTAL_SCORE"] replaces "name": "CONTAMINATION_SCORE"

jferard
  • 7,835
  • 2
  • 22
  • 35
  • I want output as Input you are taking. Input would be the list. – Harsha Biyani Mar 22 '19 at 06:50
  • @HarshaBiyani Could you elaborate? There are lists in my inputs. For every list `[a, b, c]` the output is `{"0":a, "1":b, "2":c}` (as requested). If the list has one one element: `[a]`, the output is `{"0":a}`. – jferard Mar 22 '19 at 07:26