Как я могу сгладить список, содержащий dict, рассматривая его индекс как ключ?

Я хочу сгладить дикт. Словарь может содержать списки. Таким образом, при выравнивании списков внутри словаря он должен учитывать индекс списка в качестве своего ключа.

Как я могу сделать то же самое?

Я старался:

def flatten(d, parent_key='', sep='__'):
    items = []
    for k, v in d.items():
        new_key = parent_key + sep + k if parent_key else k
        if isinstance(v, collections.MutableMapping):
            items.extend(flatten(v, new_key, sep=sep).items())
        else:
            items.append((new_key, v))
    return dict(items)

Это выравнивание dict, но игнорирование списков.

Я также пытался добавить if isinstance(v, list):, но не понимаю, как append / extend в items.

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

Вывод, который я получаю:

{
    'checksum': 'c540fcd985bf88c87e48c2bfa1df5498',
    'data__sampleMetrics__metrics': [{
        'LSL': 0,
        'USL': 3106,
        'name': 'CONTAMINATION_SCORE',
        'value': 1302,
        'UOM': 'NA'
    },{ 'demo': 11}],
    'data__sampleMetrics__name': 'DNA Library QC Metrics',
    'data__sampleMetrics__passQualityControl': 'true'
}

Что сглаживает другие вещи, кроме элементов списка.

Ожидаемый результат: он также должен сгладить список (с учетом индекса списка в качестве ключа).

{
    'checksum': 'c540fcd985bf88c87e48c2bfa1df5498',
    'data__sampleMetrics__metrics__0__LSL': 0,
    'data__sampleMetrics__metrics__0__USL': 3106,
    'data__sampleMetrics__metrics__0__name': 'CONTAMINATION_SCORE',
    'data__sampleMetrics__metrics__0__value': 1302,
    'data__sampleMetrics__metrics__0__UOM': 'NA',
    'data__sampleMetrics__metrics__1__demo': 11,
    'data__sampleMetrics__name': 'DNA Library QC Metrics',
    'data__sampleMetrics__passQualityControl': 'true'
}

Как сгладить список, содержащий словарь, рассматривая его индекс как ключ?


person Harsha Biyani    schedule 22.03.2019    source источник
comment
@PatrickArtner добавил ожидаемый результат   -  person Harsha Biyani    schedule 22.03.2019
comment
@DirtyBit: добавлен ожидаемый результат.   -  person Harsha Biyani    schedule 22.03.2019
comment
Это в двойных кавычках. Изменено так же   -  person Harsha Biyani    schedule 22.03.2019
comment
Если индекс является ключом, то не должны ли все они быть 0__LSL, 0__USL, 0__name и т. д., поскольку этот вложенный словарь является 0-м элементом списка?   -  person muru    schedule 22.03.2019
comment
@muru .. да .. правильно .. изменив вопрос.   -  person Harsha Biyani    schedule 22.03.2019
comment
Что должно произойти, если в списке "metrics" более одного словаря?   -  person martineau    schedule 22.03.2019
comment
@martineau, это тоже должно быть сглажено. 2-й элемент будет считаться 1-м индексом, 3-й будет считаться 2-м индексом и так далее. Я изменил вопрос.   -  person Harsha Biyani    schedule 22.03.2019


Ответы (2)


Вам также необходимо проверить списки — они не MutableMapings — поэтому они в настоящее время подпадают под вашу часть else: и добавляются как есть:

import collections
from itertools import chain 

def flatten(d, parent_key='', sep='__'):
    items = []
    for k, v in d.items():
        new_key = parent_key + sep + k if parent_key else k
        if isinstance(v, collections.MutableMapping):
            items.extend(flatten(v, new_key, sep=sep).items())
        elif isinstance(v, list):
            for idx, value in enumerate(v):
                items.extend(flatten(value, new_key + sep + str(idx), sep).items())
        else:
            items.append((new_key, v))
    return dict(items)

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

print flatten(data) 

Выход:

{'data__sampleMetrics__metrics__0__LSL': 0, 
 'checksum': 'c540fcd985bf88c87e48c2bfa1df5498', 
 'data__sampleMetrics__metrics__0__name': 'CONTAMINATION_SCORE', 
 'data__sampleMetrics__metrics__1__demo': 11, 
 'data__sampleMetrics__metrics__0__UOM': 'NA', 
 'data__sampleMetrics__metrics__0__USL': 3106, 
 'data__sampleMetrics__metrics__0__value': 1302, 
 'data__sampleMetrics__passQualityControl': 'true', 
 'data__sampleMetrics__name': 'DNA Library QC Metrics'}

Чтобы получить «отсортированный» вывод, вам нужно будет использовать OrderedDict в python 2.x:

из коллекций импортировать OrderedDict

data = OrderedDict(sorted(flatten(data).items()))
print data 

Выход:

OrderedDict([('checksum', 'c540fcd985bf88c87e48c2bfa1df5498'), 
             ('data__sampleMetrics__metrics__0__LSL', 0), 
             ('data__sampleMetrics__metrics__0__UOM', 'NA'), 
             ('data__sampleMetrics__metrics__0__USL', 3106), 
             ('data__sampleMetrics__metrics__0__name', 'CONTAMINATION_SCORE'), 
             ('data__sampleMetrics__metrics__0__value', 1302), 
             ('data__sampleMetrics__metrics__1__demo', 11), 
             ('data__sampleMetrics__name', 'DNA Library QC Metrics'), 
             ('data__sampleMetrics__passQualityControl', 'true')])
person Patrick Artner    schedule 22.03.2019
comment
Для python3 вы должны заменить collections.MutableMapping на collections.abc.MutableMapping, чтобы избежать предупреждения. Импорт MutableMapping напрямую из коллекций больше не поддерживается. - person Dominique Paul; 09.11.2020

Поскольку список — это не отображение, а последовательность, вам нужно добавить для него случай:

if isinstance(v, collections.MutableMapping):
    items.extend(flatten(v, new_key, sep=sep).items())
elif isinstance(v, collections.Sequence) and not isinstance(v, str):
    items.extend(sum((flatten(vv, new_key + sep + str(kk), sep).items() for kk, vv in enumerate(v)), []))
else:
    items.append((new_key, v))

Распаковка этого:

flatten(vv, new_key + sep + str(kk), sep).items() for kk, vv in enumerate(v)

получает сглаженный вывод для каждого элемента в списке с индексом, используемым для нового ключа new. Затем мы объединяем все это, чтобы получить список, и расширяем items этим. (Или вы можете перебирать каждый цикл и многократно расширять items... .)

person muru    schedule 22.03.2019
comment
Это дает мне AttributeError: 'str' object has no attribute 'items' - person Harsha Biyani; 22.03.2019
comment
elif isinstance(v, collections.Sequence) and not isinstance(v,str): - строки также являются последовательностями - их нужно исключить - person Patrick Artner; 22.03.2019