展平嵌套词典,压缩键

时间:2011-05-17 07:23:02

标签: python dictionary

假设你有一个字典:

{'a': 1,
 'c': {'a': 2,
       'b': {'x': 5,
             'y' : 10}},
 'd': [1, 2, 3]}

你会如何将其扁平化为:

{'a': 1,
 'c_a': 2,
 'c_b_x': 5,
 'c_b_y': 10,
 'd': [1, 2, 3]}

30 个答案:

答案 0 :(得分:168)

基本上就像压平嵌套列表一样,你只需要做额外的工作来按键/值迭代字典,为新字典创建新键并在最后一步创建字典。

import collections

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)

>>> flatten({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]})
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}

答案 1 :(得分:56)

原始海报需要考虑两个重要因素:

  1. 是否存在关键空间问题?例如,{'a_b':{'c':1}, 'a':{'b_c':2}}会产生{'a_b_c':???}。以下解决方案通过返回可迭代的对来避免问题。
  2. 如果性能是一个问题,密钥减少器功能(我在此称为“加入”)是否需要访问整个密钥路径,或者它是否可以在每个节点上执行O(1)工作树?如果你想说joinedKey = '_'.join(*keys),那将花费你O(N ^ 2)的运行时间。但是如果你愿意说nextKey = previousKey+'_'+thisKey,那就会得到O(N)时间。下面的解决方案允许您同时执行这两个操作(因为您只能连接所有键,然后对它们进行后处理)。
  3. (性能不太可能成为一个问题,但我会详细说明其他人关心的第二点:在实现这一点时,有许多危险的选择。如果你以递归方式执行此操作并且产生并重新产生,或者< em>任何等价物不止一次触及节点(这很容易意外),你正在做O(N ^ 2)工作而不是O(N)。这是因为你可能正在计算一个键a然后a_1然后a_1_i ...,然后计算a然后计算a_1然后a_1_ii ...,但实际上你不应该我不得不再次计算a_1。即使你没有重新计算它,重新产生它(一个“逐级”的方法)同样糟糕。一个很好的例子是考虑性能在{1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}}

    以下是我写的flattenDict(d, join=..., lift=...)函数,它可以适应多种用途,可以做你想做的事。遗憾的是,在不产生上述性能损失的情况下制作这个函数的懒惰版本相当困难(许多python内置函数如chain.from_iterable实际上并不高效,我只是在对此代码的三个不同版本进行大量测试之后才意识到这一点。这个)。

    from collections import Mapping
    from itertools import chain
    from operator import add
    
    _FLAG_FIRST = object()
    
    def flattenDict(d, join=add, lift=lambda x:x):
        results = []
        def visit(subdict, results, partialKey):
            for k,v in subdict.items():
                newKey = lift(k) if partialKey==_FLAG_FIRST else join(partialKey,lift(k))
                if isinstance(v,Mapping):
                    visit(v, results, newKey)
                else:
                    results.append((newKey,v))
        visit(d, results, _FLAG_FIRST)
        return results
    

    为了更好地了解正在发生的事情,下面是不熟悉reduce(左)的人的图表,也称为“向左折叠”。有时用初始值代替k0绘制(不是列表的一部分,传递给函数)。在这里,J是我们的join函数。我们用lift(k)预处理每个k n

                   [k0,k1,...,kN].foldleft(J)
                               /    \
                             ...    kN
                             /
           J(k0,J(k1,J(k2,k3)))
                           /  \
                          /    \
               J(J(k0,k1),k2)   k3
                        /   \
                       /     \
                 J(k0,k1)    k2
                     /  \
                    /    \
                   k0     k1
    

    这实际上与functools.reduce相同,但是我们的函数对树的所有关键路径执行此操作。

    >>> reduce(lambda a,b:(a,b), range(5))
    ((((0, 1), 2), 3), 4)
    

    演示(我将其放入docstring中):

    >>> testData = {
            'a':1,
            'b':2,
            'c':{
                'aa':11,
                'bb':22,
                'cc':{
                    'aaa':111
                }
            }
        }
    from pprint import pprint as pp
    
    >>> pp(dict( flattenDict(testData, lift=lambda x:(x,)) ))
    {('a',): 1,
     ('b',): 2,
     ('c', 'aa'): 11,
     ('c', 'bb'): 22,
     ('c', 'cc', 'aaa'): 111}
    
    >>> pp(dict( flattenDict(testData, join=lambda a,b:a+'_'+b) ))
    {'a': 1, 'b': 2, 'c_aa': 11, 'c_bb': 22, 'c_cc_aaa': 111}    
    
    >>> pp(dict( (v,k) for k,v in flattenDict(testData, lift=hash, join=lambda a,b:hash((a,b))) ))
    {1: 12416037344,
     2: 12544037731,
     11: 5470935132935744593,
     22: 4885734186131977315,
     111: 3461911260025554326}
    

    性能:

    from functools import reduce
    def makeEvilDict(n):
        return reduce(lambda acc,x:{x:acc}, [{i:0 for i in range(n)}]+range(n))
    
    import timeit
    def time(runnable):
        t0 = timeit.default_timer()
        _ = runnable()
        t1 = timeit.default_timer()
        print('took {:.2f} seconds'.format(t1-t0))
    
    >>> pp(makeEvilDict(8))
    {7: {6: {5: {4: {3: {2: {1: {0: {0: 0,
                                     1: 0,
                                     2: 0,
                                     3: 0,
                                     4: 0,
                                     5: 0,
                                     6: 0,
                                     7: 0}}}}}}}}}
    
    import sys
    sys.setrecursionlimit(1000000)
    
    forget = lambda a,b:''
    
    >>> time(lambda: dict(flattenDict(makeEvilDict(10000), join=forget)) )
    took 0.10 seconds
    >>> time(lambda: dict(flattenDict(makeEvilDict(100000), join=forget)) )
    [1]    12569 segmentation fault  python
    
    ......叹息,不要以为那是我的错......


    [由于审核问题导致的不重要的历史记录]

    关于涉嫌Flatten a dictionary of dictionaries (2 levels deep) of lists in Python的重复:

    该问题的解决方案可以通过sorted( sum(flatten(...),[]) )来实现。反过来是不可能的:虽然flatten(...)确实可以通过映射高阶累加器从所谓的副本中恢复,但是无法恢复密钥。 (编辑:事实证明,所谓的重复所有者的问题是完全不同的,因为它只处理2级深度的字典,尽管该页面上的答案之一给出了一般解决方案。)

答案 2 :(得分:26)

或者,如果您已经在使用pandas,可以使用json_normalize()这样做:

import pandas as pd

d = {'a': 1,
     'c': {'a': 2, 'b': {'x': 5, 'y' : 10}},
     'd': [1, 2, 3]}

df = pd.io.json.json_normalize(d, sep='_')

print(df.to_dict(orient='records')[0])

输出:

{'a': 1, 'c_a': 2, 'c_b_x': 5, 'c_b_y': 10, 'd': [1, 2, 3]}

答案 3 :(得分:21)

这是一种“功能性”,“一线”的实现。它是递归的,基于条件表达式和字典理解。

def flatten_dict(dd, separator='_', prefix=''):
    return { prefix + separator + k if prefix else k : v
             for kk, vv in dd.items()
             for k, v in flatten_dict(vv, separator, kk).items()
             } if isinstance(dd, dict) else { prefix : dd }

测试:

In [2]: flatten_dict({'abc':123, 'hgf':{'gh':432, 'yu':433}, 'gfd':902, 'xzxzxz':{"432":{'0b0b0b':231}, "43234":1321}}, '.')
Out[2]: 
{'abc': 123,
 'gfd': 902,
 'hgf.gh': 432,
 'hgf.yu': 433,
 'xzxzxz.432.0b0b0b': 231,
 'xzxzxz.43234': 1321}

答案 4 :(得分:12)

代码:

test = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}

def parse_dict(init, lkey=''):
    ret = {}
    for rkey,val in init.items():
        key = lkey+rkey
        if isinstance(val, dict):
            ret.update(parse_dict(val, key+'_'))
        else:
            ret[key] = val
    return ret

print(parse_dict(test,''))

结果:

$ python test.py
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}

我正在使用python3.2,更新你的python版本。

答案 5 :(得分:11)

如果您使用的是pandas,则pandas.io.json.normalize中隐藏了一个名为nested_to_record的功能,可以完全执行此操作。

from pandas.io.json.normalize import nested_to_record    

flat = nested_to_record(my_dict, sep='_')

答案 6 :(得分:6)

这不仅限于字典,而是限制实现.items()的每种映射类型。因为它避免了if条件,所以更快。然而,归功于伊姆兰:

def flatten(d, parent_key=''):
    items = []
    for k, v in d.items():
        try:
            items.extend(flatten(v, '%s%s_' % (parent_key, k)).items())
        except AttributeError:
            items.append(('%s%s' % (parent_key, k), v))
    return dict(items)

答案 7 :(得分:5)

并不是OP的要求,但是很多人来这里寻求扁平化嵌套的JSON数据的方法,这些数据可以具有嵌套的键值json对象和数组以及数组内部的json对象,依此类推。 JSON不包含元组,因此我们不必为此烦恼。

我发现comment by @roneo到列表包含answer posted by @Imran的实现:

https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8

import collections
def flatten(dictionary, parent_key=False, separator='.'):
    """
    Turn a nested dictionary into a flattened dictionary
    :param dictionary: The dictionary to flatten
    :param parent_key: The string to prepend to dictionary's keys
    :param separator: The string used to separate flattened keys
    :return: A flattened dictionary
    """

    items = []
    for key, value in dictionary.items():
        new_key = str(parent_key) + separator + key if parent_key else key
        if isinstance(value, collections.MutableMapping):
            items.extend(flatten(value, new_key, separator).items())
        elif isinstance(value, list):
            for k, v in enumerate(value):
                items.extend(flatten({str(k): v}, new_key).items())
        else:
            items.append((new_key, value))
    return dict(items)

测试:

flatten({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3] })

>> {'a': 1, 'c.a': 2, 'c.b.x': 5, 'c.b.y': 10, 'd.0': 1, 'd.1': 2, 'd.2': 3}

要完成我需要做的工作:我将任何复杂的json扔给我,并为我弄平了。

所有功劳归于https://github.com/ScriptSmith

答案 8 :(得分:4)

Python3.5中功能和高性能解决方案怎么样?

from functools import reduce


def _reducer(items, key, val, pref):
    if isinstance(val, dict):
        return {**items, **flatten(val, pref + key)}
    else:
        return {**items, pref + key: val}

def flatten(d, pref=''):
    return(reduce(
        lambda new_d, kv: _reducer(new_d, *kv, pref), 
        d.items(), 
        {}
    ))

性能更高:

def flatten(d, pref=''):
    return(reduce(
        lambda new_d, kv: \
            isinstance(kv[1], dict) and \
            {**new_d, **flatten(kv[1], pref + kv[0])} or \
            {**new_d, pref + kv[0]: kv[1]}, 
        d.items(), 
        {}
    ))

使用中:

my_obj = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y': 10}}, 'd': [1, 2, 3]}

print(flatten(my_obj)) 
# {'d': [1, 2, 3], 'cby': 10, 'cbx': 5, 'ca': 2, 'a': 1}

答案 9 :(得分:4)

简化嵌套词典的简单功能。对于Python 3,将.iteritems()替换为.items()

def flatten_dict(init_dict):
    res_dict = {}
    if type(init_dict) is not dict:
        return res_dict

    for k, v in init_dict.iteritems():
        if type(v) == dict:
            res_dict.update(flatten_dict(v))
        else:
            res_dict[k] = v

    return res_dict

想法/要求是: 获取平面词典,不保留父键。

使用示例:

dd = {'a': 3, 
      'b': {'c': 4, 'd': 5}, 
      'e': {'f': 
                 {'g': 1, 'h': 2}
           }, 
      'i': 9,
     }

flatten_dict(dd)

>> {'a': 3, 'c': 4, 'd': 5, 'g': 1, 'h': 2, 'i': 9}

保持父键也很简单。

答案 10 :(得分:4)

使用生成器的我的Python 3.3解决方案:

def flattenit(pyobj, keystring=''):
   if type(pyobj) is dict:
     if (type(pyobj) is dict):
         keystring = keystring + "_" if keystring else keystring
         for k in pyobj:
             yield from flattenit(pyobj[k], keystring + k)
     elif (type(pyobj) is list):
         for lelm in pyobj:
             yield from flatten(lelm, keystring)
   else:
      yield keystring, pyobj

my_obj = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y': 10}}, 'd': [1, 2, 3]}

#your flattened dictionary object
flattened={k:v for k,v in flattenit(my_obj)}
print(flattened)

# result: {'c_b_y': 10, 'd': [1, 2, 3], 'c_a': 2, 'a': 1, 'c_b_x': 5}

答案 11 :(得分:3)

Davoud的解决方案非常好,但是当嵌套的dict也包含dicts列表时,它没有给出令人满意的结果,但他的代码适用于那种情况:

def flatten_dict(d):
    items = []
    for k, v in d.items():
        try:
            if (type(v)==type([])): 
                for l in v: items.extend(flatten_dict(l).items())
            else: 
                items.extend(flatten_dict(v).items())
        except AttributeError:
            items.append((k, v))
    return dict(items)

答案 12 :(得分:3)

这与imran和ralu的答案类似。它不使用生成器,而是使用带有闭包的递归:

def flatten_dict(d, separator='_'):
  final = {}
  def _flatten_dict(obj, parent_keys=[]):
    for k, v in obj.iteritems():
      if isinstance(v, dict):
        _flatten_dict(v, parent_keys + [k])
      else:
        key = separator.join(parent_keys + [k])
        final[key] = v
  _flatten_dict(d)
  return final

>>> print flatten_dict({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]})
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}

答案 13 :(得分:2)

这是一种优雅的就地替换算法。使用Python 2.7和Python 3.5进行测试。使用点字符作为分隔符。

def flatten_json(json):
    if type(json) == dict:
        for k, v in list(json.items()):
            if type(v) == dict:
                flatten_json(v)
                json.pop(k)
                for k2, v2 in v.items():
                    json[k+"."+k2] = v2

示例:

d = {'a': {'b': 'c'}}                   
flatten_json(d)
print(d)
unflatten_json(d)
print(d)

输出:

{'a.b': 'c'}
{'a': {'b': 'c'}}

我发布了此代码here以及匹配的unflatten_json函数。

答案 14 :(得分:2)

上面的答案非常有效。我以为我会添加我写的不平坦函数:

def unflatten(d):
    ud = {}
    for k, v in d.items():
        context = ud
        for sub_key in k.split('_')[:-1]:
            if sub_key not in context:
                context[sub_key] = {}
            context = context[sub_key]
        context[k.split('_')[-1]] = v
    return ud

注意:这不会解释密钥中已经存在的“_”,就像扁平的对应物一样。

答案 15 :(得分:2)

如果您想平铺嵌套字典并需要所有唯一键列表,那么这里是解决方法:

def flat_dict_return_unique_key(data, unique_keys=set()):
    if isinstance(data, dict):
        [unique_keys.add(i) for i in data.keys()]
        for each_v in data.values():
            if isinstance(each_v, dict):
                flat_dict_return_unique_key(each_v, unique_keys)
    return list(set(unique_keys))

答案 16 :(得分:1)

在简单的嵌套列表式递归中使用dict.popitem():

def flatten(d):
    if d == {}:
        return d
    else:
        k,v = d.popitem()
        if (dict != type(v)):
            return {k:v, **flatten(d)}
        else:
            flat_kv = flatten(v)
            for k1 in list(flat_kv.keys()):
                flat_kv[k + '_' + k1] = flat_kv[k1]
                del flat_kv[k1]
            return {**flat_kv, **flatten(d)}

答案 17 :(得分:1)

利用递归,使其简单易懂:

def flatten_dict(dictionary, accumulator=None, parent_key=None, separator="."):
    if accumulator is None:
        accumulator = {}

    for k, v in dictionary.items():
        k = f"{parent_key}{separator}{k}" if parent_key else k
        if isinstance(v, dict):
            flatten_dict(dictionary=v, accumulator=accumulator, parent_key=k)
            continue

        accumulator[k] = v

    return accumulator

呼叫很简单:

new_dict = flatten_dict(dictionary)

new_dict = flatten_dict(dictionary, separator="_")

如果要更改默认分隔符。

一些细目:

首次调用该函数时,仅通过我们要展平的dictionary来调用该函数。 accumulator参数在此处支持递归,稍后我们将介绍。因此,我们将accumulator实例化为一个空字典,在其中将来自原始dictionary的所有嵌套值放入。

if accumulator is None:
    accumulator = {}

当我们迭代字典的值时,我们为每个值构造一个键。第一次调用时,parent_key参数将为None,而对于每个嵌套字典,它将包含指向它的键,因此我们将该键放在前面。

k = f"{parent_key}{separator}{k}" if parent_key else k

如果键v指向的值k是一个字典,则函数将调用自身,并传递嵌套字典accumulator(通过引用传递,因此对它所做的所有更改都在同一实例上完成)和键k,以便我们可以构造级联键。注意continue语句。我们想跳过if块之外的下一行,以使嵌套的字典不会在键accumulator下的k中结束。

if isinstance(v, dict):
    flatten_dict(dict=v, accumulator=accumulator, parent_key=k)
    continue

那么,如果值v不是字典,该怎么办?只需将其放在accumulator内即可。

accumulator[k] = v

完成后,我们只返回accumulator,而原始dictionary的参数保持不变。

注意

这仅适用于以字符串为键的字典。它将与实现__repr__方法的可哈希对象一起使用,但会产生不需要的结果。

答案 18 :(得分:1)

def flatten_nested_dict(_dict, _str=''):
    '''
    recursive function to flatten a nested dictionary json
    '''
    ret_dict = {}
    for k, v in _dict.items():
        if isinstance(v, dict):
            ret_dict.update(flatten_nested_dict(v, _str = '_'.join([_str, k]).strip('_')))
        elif isinstance(v, list):
            for index, item in enumerate(v):
                if isinstance(item, dict):
                    ret_dict.update(flatten_nested_dict(item,  _str= '_'.join([_str, k, str(index)]).strip('_')))
                else:
                    ret_dict['_'.join([_str, k, str(index)]).strip('_')] = item
        else:
            ret_dict['_'.join([_str, k]).strip('_')] = v
    return ret_dict

答案 19 :(得分:1)

def flatten(unflattened_dict, separator='_'):
    flattened_dict = {}

    for k, v in unflattened_dict.items():
        if isinstance(v, dict):
            sub_flattened_dict = flatten(v, separator)
            for k2, v2 in sub_flattened_dict.items():
                flattened_dict[k + separator + k2] = v2
        else:
            flattened_dict[k] = v

    return flattened_dict

答案 20 :(得分:1)

使用生成器:

def flat_dic_helper(prepand,d):
    if len(prepand) > 0:
        prepand = prepand + "_"
    for k in d:
        i=d[k]
        if type(i).__name__=='dict':
            r = flat_dic_helper(prepand+k,i)
            for j in r:
                yield j
        else:
            yield (prepand+k,i)

def flat_dic(d): return dict(flat_dic_helper("",d))

d={'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
print(flat_dic(d))


>> {'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}

答案 21 :(得分:0)

实际上我最近写了一个名为cherrypicker的程序包来处理这种确切的事情,因为我不得不经常这样做!

我认为以下代码将为您提供确切的帮助:

from cherrypicker import CherryPicker

dct = {
    'a': 1,
    'c': {
        'a': 2,
        'b': {
            'x': 5,
            'y' : 10
        }
    },
    'd': [1, 2, 3]
}

picker = CherryPicker(dct)
picker.flatten().get()

您可以通过以下方式安装软件包:

pip install cherrypicker

... https://github.com/webpack-contrib/html-loader#advanced-options上有更多文档和指南。

其他方法可能更快,但是此程序包的优先级是使这些任务迅速容易。如果您确实有大量要展平的对象,则还可以告诉CherryPicker使用并行处理来加快处理速度。

答案 22 :(得分:0)

Flatten nested dictionaries, compressing keys与max_level和自定义减速器的差异。

  def flatten(d, max_level=None, reducer='tuple'):
      if reducer == 'tuple':
          reducer_seed = tuple()
          reducer_func = lambda x, y: (*x, y)
      else:
          raise ValueError(f'Unknown reducer: {reducer}')

      def impl(d, pref, level):
        return reduce(
            lambda new_d, kv:
                (max_level is None or level < max_level)
                and isinstance(kv[1], dict)
                and {**new_d, **impl(kv[1], reducer_func(pref, kv[0]), level + 1)}
                or {**new_d, reducer_func(pref, kv[0]): kv[1]},
                d.items(),
            {}
        )

      return impl(d, reducer_seed, 0)

答案 23 :(得分:0)

我总是更喜欢通过dict访问.items()对象,因此为了展平字典,我使用以下递归生成器flat_items(d)。如果您想再次拥有dict,只需将其包装如下:flat = dict(flat_items(d))

def flat_items(d, key_separator='.'):
    """
    Flattens the dictionary containing other dictionaries like here: https://stackoverflow.com/questions/6027558/flatten-nested-python-dictionaries-compressing-keys

    >>> example = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
    >>> flat = dict(flat_items(example, key_separator='_'))
    >>> assert flat['c_b_y'] == 10
    """
    for k, v in d.items():
        if type(v) is dict:
            for k1, v1 in flat_items(v, key_separator=key_separator):
                yield key_separator.join((k, k1)), v1
        else:
            yield k, v

答案 24 :(得分:0)

我在考虑UserDict的子类来自动平键。

import numpy as np
import cv2
element = np.ones((21, 1))

new_image = cv2.morphologyEx(origin_image, cv2.MORPH_TOPHAT, element, iterations = 1)

‌ 可以随时添加密钥或使用标准dict实例添加密钥的优点,而不会感到惊讶:

class FlatDict(UserDict):
    def __init__(self, *args, separator='.', **kwargs):
        self.separator = separator
        super().__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            for k1, v1 in FlatDict(value, separator=self.separator).items():
                super().__setitem__(f"{key}{self.separator}{k1}", v1)
        else:
            super().__setitem__(key, value)

答案 25 :(得分:0)

如果您不介意递归函数,这是一个解决方案。如果您希望保留一个或多个值,我也可以自由地包含一个 exclusion 参数。

代码:

def flatten_dict(dictionary, exclude = [], delimiter ='_'):
    flat_dict = dict()
    for key, value in dictionary.items():
        if isinstance(value, dict) and key not in exclude:
            flatten_value_dict = flatten_dict(value, exclude, delimiter)
            for k, v in flatten_value_dict.items():
                flat_dict[f"{key}{delimiter}{k}"] = v
        else:
            flat_dict[key] = value
    return flat_dict

用法:

d = {'a':1, 'b':[1, 2], 'c':3, 'd':{'a':4, 'b':{'a':7, 'b':8}, 'c':6}, 'e':{'a':1,'b':2}}
flat_d = flatten_dict(dictionary=d, exclude=['e'], delimiter='.')
print(flat_d)

输出:

{'a': 1, 'b': [1, 2], 'c': 3, 'd.a': 4, 'd.b.a': 7, 'd.b.b': 8, 'd.c': 6, 'e': {'a': 1, 'b': 2}}

答案 26 :(得分:0)

我尝试了此页面上的某些解决方案-尽管不是全部-但尝试的解决方案无法处理dict的嵌套列表。

考虑这样的字典:

d = {
        'owner': {
            'name': {'first_name': 'Steven', 'last_name': 'Smith'},
            'lottery_nums': [1, 2, 3, 'four', '11', None],
            'address': {},
            'tuple': (1, 2, 'three'),
            'tuple_with_dict': (1, 2, 'three', {'is_valid': False}),
            'set': {1, 2, 3, 4, 'five'},
            'children': [
                {'name': {'first_name': 'Jessica',
                          'last_name': 'Smith', },
                 'children': []
                 },
                {'name': {'first_name': 'George',
                          'last_name': 'Smith'},
                 'children': []
                 }
            ]
        }
    }

这是我的临时解决方案:

def flatten_dict(input_node: dict, key_: str = '', output_dict: dict = {}):
    if isinstance(input_node, dict):
        for key, val in input_node.items():
            new_key = f"{key_}.{key}" if key_ else f"{key}"
            flatten_dict(val, new_key, output_dict)
    elif isinstance(input_node, list):
        for idx, item in enumerate(input_node):
            flatten_dict(item, f"{key_}.{idx}", output_dict)
    else:
        output_dict[key_] = input_node
    return output_dict

产生:

{
  owner.name.first_name: Steven,
  owner.name.last_name: Smith,
  owner.lottery_nums.0: 1,
  owner.lottery_nums.1: 2,
  owner.lottery_nums.2: 3,
  owner.lottery_nums.3: four,
  owner.lottery_nums.4: 11,
  owner.lottery_nums.5: None,
  owner.tuple: (1, 2, 'three'),
  owner.tuple_with_dict: (1, 2, 'three', {'is_valid': False}),
  owner.set: {1, 2, 3, 4, 'five'},
  owner.children.0.name.first_name: Jessica,
  owner.children.0.name.last_name: Smith,
  owner.children.1.name.first_name: George,
  owner.children.1.name.last_name: Smith,
}

一个临时解决方案,它并不完美。
注意:

  • 它不会保留空的字典,例如address: {} k / v对。

  • 它不会拼合嵌套元组中的字典-尽管使用python元组的行为类似于列表这一事实很容易添加。

答案 27 :(得分:0)

这是使用堆栈的解决方案。没有递归。

def flatten_nested_dict(nested):
    stack = list(nested.items())
    ans = {}
    while stack:
        key, val = stack.pop()
        if isinstance(val, dict):
            for sub_key, sub_val in val.items():
                stack.append((f"{key}_{sub_key}", sub_val))
        else:
            ans[key] = val
    return ans

答案 28 :(得分:0)

你可以使用递归来扁平化你的字典。

import collections


def flatten(
    nested_dict,
    seperator='.',
    name=None,
):
    flatten_dict = {}

    if not nested_dict:
        return flatten_dict

    if isinstance(
        nested_dict,
        collections.abc.MutableMapping,
    ):
        for key, value in nested_dict.items():
            if name is not None:
                flatten_dict.update(
                    flatten(
                        nested_dict=value,
                        seperator=seperator,
                        name=f'{name}{seperator}{key}',
                    ),
                )
            else:
                flatten_dict.update(
                    flatten(
                        nested_dict=value,
                        seperator=seperator,
                        name=key,
                    ),
                )
    else:
        flatten_dict[name] = nested_dict

    return flatten_dict


if __name__ == '__main__':
    nested_dict = {
        1: 'a',
        2: {
            3: 'c',
            4: {
                5: 'e',
            },
            6: [1, 2, 3, 4, 5, ],
        },
    }

    print(
        flatten(
            nested_dict=nested_dict,
        ),
    )

输出:

{
   "1":"a",
   "2.3":"c",
   "2.4.5":"e",
   "2.6":[1, 2, 3, 4, 5]
}

答案 29 :(得分:-1)

仅使用python-benedict,它是一个dict子类,提供了许多功能,其中包括flatten方法。可以使用pip:pip install python-benedict

进行安装

https://github.com/fabiocaccamo/python-benedict#flatten

from benedict import benedict 

d = benedict(data)
f = d.flatten(separator='_')