假设您有一个像这样的字典:
{'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]}
假设您有一个像这样的字典:
{'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]}
Answers:
基本上与展平嵌套列表的方法相同,只需要做额外的工作即可按键/值迭代dict,为新字典创建新键并在最后一步创建字典。
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}
isinstance
一个try..except
块,即使它不是从派生的,这也适用于任何映射dict
。
collections.MutableMapping
以使其更通用。但是对于<2.6的Python,try..except
可能是最好的选择。
if isinstance(v, collections.MutableMapping):
为if v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k
假设键始终是字符串,否则它将提高TypeError: cannot concatenate 'str' and [other] objects
。但是,您可以通过简单地强制k
使用字符串(str(k)
),或将键串联到一个元组而不是字符串中来解决此问题(元组也可以是dict键)。
原始海报需要考虑两个主要方面:
{'a_b':{'c':1}, 'a':{'b_c':2}}
将导致{'a_b_c':???}
。下面的解决方案通过返回可迭代的对来避免该问题。joinedKey = '_'.join(*keys)
,则将花费O(N ^ 2)的运行时间。但是,如果您愿意说nextKey = previousKey+'_'+thisKey
,那将使您节省O(N)的时间。下面的解决方案允许您同时执行这两个操作(因为您只能将所有键连接起来,然后对其进行后处理)。(性能不太可能成为问题,但在其他人关心的情况下,我将详细说明第二点:在实施此方法时,存在许多危险的选择。如果您递归执行此操作,则产生并重新产生收益,或者涉及到的任何等效内容节点超过一次(这是很容易不小心做),你正在做的潜在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=...)
,该函数可以适应多种用途,并且可以执行您想要的操作。可悲的是,要使该函数成为一个懒惰的版本而不引起上述性能损失是相当困难的(许多像chain.from_iterable这样的python内置函数实际上并不是很有效,我只有在对该代码的三个不同版本进行了广泛的测试之后才意识到这一点。这个)。
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)
。
[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的所谓副本,它是Python中列表的字典词典(深度2级):
该问题的解决方案可以通过这样做来实现sorted( sum(flatten(...),[]) )
。反向是不可能的:虽然这是事实,所述值的flatten(...)
可以从重复的指称通过映射高阶累加器被回收,一个不能恢复键。(编辑:事实证明,所谓的重复所有者的问题是完全不同的,尽管该页面上的答案之一给出了一般的解决方案,但它仅处理深度为2级的字典。)
或者,如果您已经在使用熊猫,则可以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]}
如果您使用pandas
有隐藏在一个函数pandas.io.json._normalize
1名为nested_to_record
该做到这一点没错。
from pandas.io.json._normalize import nested_to_record
flat = nested_to_record(my_dict, sep='_')
1在熊猫版本0.24.x
和较旧的版本中pandas.io.json.normalize
(不带_
)
from pandas.io.json._normalize import nested_to_record
。注意_
之前的下划线()normalize
。
0.25.x
已在中更改,我已经更新了答案。:)
这是一种“功能”,“单线”实现。它是递归的,并且基于条件表达式和dict理解。
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}
('hgf',2)
测试中的第二个键TypeError
+
运算符的字符串。对于其他任何事情,您都需要适应prefix + separator + k
适当的函数调用以组成对象。
{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
码:
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版本。
lkey=''
在函数定义中指定默认值,而不是在调用函数时指定。查看这方面的其他答案。
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}
reduce
如果您需要减少字典,我仍然认为这很棒。我更新了答案。现在应该看起来更pythonic。
这不限于字典,而是每个实现.items()的映射类型。更进一步,因为它避免了if条件。尽管如此,功劳归功于Imran:
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)
d
不是一个dict
未实现的自定义映射类型,则items
您的函数将立即失败。因此,它不适用于每种映射类型,仅适用于那些实现的映射类型items()
。
items
?我很好奇看到一个。
我的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}
简单的函数可以拼合嵌套词典。对于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}
保持父键也很简单。
利用递归,使其简单易懂:
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
under键结束k
。
if isinstance(v, dict):
flatten_dict(dict=v, accumulator=accumulator, parent_key=k)
continue
那么,如果值v
不是字典,该怎么办?只需将其保持不变即可accumulator
。
accumulator[k] = v
完成后,我们只返回accumulator
,而原始dictionary
参数保持不变。
注意
这仅适用于以字符串为键的字典。它将与实现该__repr__
方法的可哈希对象一起使用,但会产生不需要的结果。
这与伊姆兰和拉鲁的答案相似。它不使用生成器,而是使用闭包进行递归:
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}
_flatten_dict
永远不会返回,也永远不会返回。也许可以将其称为子功能或封闭功能。
Davoud的解决方案非常好,但是当嵌套dict也包含字典列表时,不会给出令人满意的结果,但是他的代码适用于这种情况:
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)
type([])
以避免对的每个项目进行函数调用dict
。
isinstance(v, list)
改用
这是一种优雅的就地替换算法。经过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'}}
我在这里发布了此代码以及匹配unflatten_json
功能。
如果您想使用平面嵌套字典并需要所有唯一键列表,那么这里是解决方案:
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))
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
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
我在考虑UserDict的子类来自动平键。
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)
keys可以立即添加密钥或使用标准dict实例添加密钥的优点,这并不奇怪:
>>> fd = FlatDict(
... {
... 'person': {
... 'sexe': 'male',
... 'name': {
... 'first': 'jacques',
... 'last': 'dupond'
... }
... }
... }
... )
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond'}
>>> fd['person'] = {'name': {'nickname': 'Bob'}}
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond', 'person.name.nickname': 'Bob'}
>>> fd['person.name'] = {'civility': 'Dr'}
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond', 'person.name.nickname': 'Bob', 'person.name.civility': 'Dr'}
使用生成器:
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}
type(i).__name__=='dict'
可以替换为,type(i) is dict
甚至更好isinstance(d, dict)
(或Mapping
/ MutableMapping
)。
在类似嵌套列表的简单递归中使用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)}
并非完全是OP所要求的,而是许多人来这里寻找扁平化现实世界中嵌套的JSON数据的方法,这些数据可以具有嵌套的键值json对象和数组以及数组内部的json对象,依此类推。JSON不包含元组,因此我们不必为此烦恼。
我发现@roneo对@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}
完成我需要完成的工作的Annd:我在此抛出任何复杂的json,并为我整理了它。
所有积分均来自https://github.com/ScriptSmith。
实际上我最近写了一个叫做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://cherrypicker.readthedocs.io上有更多文档和指南。
其他方法可能更快,但是此程序包的优先级是使此类任务变得容易。如果确实有大量要展平的对象,则还可以告诉CherryPicker使用并行处理来加快处理速度。
我总是喜欢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: /programming/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
此Flatten嵌套字典的变体,使用max_level和自定义reducer 压缩键。
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)
如果您不介意递归函数,这是一个解决方案。如果您希望保留一个或多个值,我也可以自由地包含一个排除参数。
码:
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}}
我尝试了此页面上的某些解决方案-尽管不是全部-但尝试的解决方案无法处理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元组的行为类似于列表这一事实很容易添加。
只需使用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='_')