मान लीजिए कि आपके पास एक शब्दकोश है:
{'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]}
जवाबों:
मूल रूप से उसी तरह जब आप एक नेस्टेड सूची को समतल करेंगे, तो आपको केवल कुंजी / मान द्वारा ताना को पुनरावृत्त करने के लिए अतिरिक्त काम करना होगा, अपने नए शब्दकोश के लिए नई कुंजी बनाने और अंतिम चरण पर शब्दकोश बनाने के लिए।
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 के लिए, 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)) के लिए ज़बरदस्ती करके , या एक स्ट्रिंग के बजाय टूपल में कुंजी को घुमाएं (ट्यूपल्स तानाशाही कुंजी भी हो सकते हैं)।
मूल पोस्टर पर विचार करने के लिए दो बड़े विचार हैं:
{'a_b':{'c':1}, 'a':{'b_c':2}}परिणाम होगा {'a_b_c':???}। नीचे दिए गए समाधान जोड़े की पुनरावृत्ति को वापस करके समस्या को हल करते हैं।joinedKey = '_'.join(*keys), तो आपको O (N ^ 2) को चलाने में समय लगेगा। हालाँकि यदि आप यह कहना चाहते हैं nextKey = previousKey+'_'+thisKeyकि आपको O (N) समय मिलता है। नीचे दिए गए समाधान से आप दोनों कर सकते हैं (चूंकि आप सभी कुंजियों को केवल संक्षिप्त कर सकते हैं, फिर उन्हें पोस्टप्रोसेस कर सकते हैं)।(प्रदर्शन की संभावना नहीं है कि कोई समस्या हो, लेकिन मैं दूसरे बिंदु पर विस्तार से बताऊंगा कि कोई और परवाह करता है: इसे लागू करने में, कई खतरनाक विकल्प हैं। यदि आप इसे पुनरावर्ती करते हैं और उपज और पुन: उपज, या कुछ भी जो छूता है के बराबर है। एक बार से अधिक नोड्स (जो गलती से ऐसा करने के लिए काफी आसान है), तो आप संभवतः हे (एन ^ 2) बल्कि हे (एन) की तुलना में काम कर रहे हैं। इसका कारण यह है हो सकता है आप एक महत्वपूर्ण गणना कर रहे हैं 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=...)जो मैंने लिखा था जिसे कई उद्देश्यों के लिए अनुकूलित किया जा सकता है और आप जो चाहते हैं वह कर सकते हैं। अफसोस की बात है कि उपरोक्त प्रदर्शन का दंड दिए बिना इस समारोह का एक आलसी संस्करण बनाना काफी कठिन है (कई अजगर बिल्डरों जैसे कि चेन ।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कार्य है। हम प्रत्येक k n के साथ प्रीप्रोसेस करते हैं 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
... आह, यह मत सोचो कि एक मेरी गलती है ...
[मॉडरेशन मुद्दों के कारण महत्वहीन ऐतिहासिक नोट]
पायथन में सूचियों के एक शब्दकोश (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]}
यदि आप उपयोग कर रहे हैं तो 1pandas में छिपा हुआ एक फंक्शन है जिसे बुलाया जाता है।pandas.io.json._normalizenested_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, मैंने उत्तर अपडेट कर दिया है। :)
यहां एक प्रकार का "कार्यात्मक", "वन-लाइनर" कार्यान्वयन है। यह पुनरावर्ती है, और एक सशर्त अभिव्यक्ति और एक तानाशाही समझ पर आधारित है।
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)लिए आपके टेस्ट थ्रो में 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 का उपयोग कर रहा हूं, अपने अजगर के संस्करण के लिए अपडेट करें।
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है कि आपको शब्दकोशों को कम करने की आवश्यकता है। मैंने जवाब अपडेट किया। अब थोड़ा और पाइथोनिक दिखना चाहिए।
यह केवल शब्दकोशों तक ही सीमित नहीं है, बल्कि प्रत्येक मैपिंग प्रकार जो लागू करता है ।items ()। इसके अलावा अगर यह एक शर्त है तो यह और तेज होता है। फिर भी श्रेय इमरान को जाता है:
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? मैं एक को देखने के लिए उत्सुक हूँ।
जनरेटर का उपयोग करके मेरा पायथन 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}
नेस्टेड शब्दकोशों को समतल करने के लिए सरल कार्य। पायथन 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अंडर की में समाप्त न हो 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}
दावॉड का समाधान बहुत अच्छा है लेकिन संतोषजनक परिणाम नहीं देता है जब नेस्टेड डिक्टेट में भी डिटॉक्स की सूची होती है, लेकिन उसके कोड को उस मामले के लिए अनुकूलित किया जाना चाहिए:
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)इसके बजाय का उपयोग करें
उपरोक्त उत्तर वास्तव में अच्छी तरह से काम करते हैं। बस मैंने सोचा था कि मैंने जो अनफ़ल्टेन फ़ंक्शन लिखा था, उसे जोड़ूंगा:
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
नोट: यह '_' के लिए पहले से ही कुंजी में मौजूद नहीं है, सपाट समकक्षों की तरह।
यहाँ सुरुचिपूर्ण, इन-प्लेस प्रतिस्थापन के लिए एक एल्गोरिथ्म है। पायथन 2.7 और पायथन 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)
Fly फायदे यह है कि चाबियाँ मक्खी पर जोड़ी जा सकती हैं, या बिना आश्चर्य के मानक तानाशाहों का उपयोग कर सकते हैं:
>>> 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) बदला जा सकता है ।
तानाशाही का उपयोग करना। सीधे-सीधे नेस्टेड-सूची जैसी पुनरावृत्ति में)
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)}
ओपी ने पूछा कि वास्तव में क्या नहीं है, लेकिन बहुत से लोग यहां आ रहे हैं, जो वास्तविक दुनिया के नेस्टेड JSON डेटा को समतल करने के तरीकों की तलाश कर रहे हैं, जिनमें नेस्ट-वैल्यू json ऑब्जेक्ट्स और arrays और json ऑब्जेक्ट्स सरणियों के अंदर और इतने पर नेस्टेड हो सकते हैं। JSON में tuples शामिल नहीं है, इसलिए हमें उन पर झल्लाहट नहीं करनी है।
मैं इस सूची में शामिल किए जाने-के एक कार्यान्वयन पाया @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}
और वह काम जो मुझे करने की आवश्यकता है: मैं इस पर कोई भी जटिल जुबान फेंकता हूं और यह मेरे लिए इसे समतल कर देता है।
सभी क्रेडिट https://github.com/ScriptSmith पर ।
मैंने वास्तव में चेरीपिकर नामक एक पैकेज हाल ही में इस तरह की चीज़ से निपटने के लिए लिखा था क्योंकि मुझे ऐसा अक्सर करना पड़ता था!
मुझे लगता है कि निम्नलिखित कोड आपको वही देगा जो आप इसके बाद कर रहे हैं:
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 पर अधिक डॉक्स और मार्गदर्शन है ।
अन्य विधियां तेज हो सकती हैं, लेकिन इस पैकेज की प्राथमिकता ऐसे कार्यों को आसान बनाना है । यदि आपके पास समतल करने के लिए वस्तुओं की एक बड़ी सूची है, तो आप चीजों को गति देने के लिए समानांतर प्रसंस्करण का उपयोग करने के लिए चेरीपिकर को भी बता सकते हैं।
मैं हमेशा एक्सेस 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 नेस्टेड डिक्शनरी का वेरिएशन , अधिकतम_लेवल और कस्टम रिड्यूसर के साथ कंप्रेसिंग कीज ।
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}}
मैंने इस पृष्ठ पर कुछ समाधानों की कोशिश की - हालांकि सभी नहीं - लेकिन जिन लोगों ने कोशिश की, वे तानाशाह की नेस्टेड सूची को संभालने में विफल रहे।
इस तरह एक तानाशाही पर विचार करें:
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-benedict, यह एक तानाशाह उपवर्ग है जो कई विशेषताएं प्रदान करता है, जिसमें एक flattenविधि भी शामिल है । पाइप का उपयोग करके इसे स्थापित करना संभव है:pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
from benedict import benedict
d = benedict(data)
f = d.flatten(separator='_')