ফ্ল্যাটেড নেস্টেড ডিকশনারি, সংক্ষেপণ কীগুলি


172

ধরুন আপনার কাছে এর মতো একটি অভিধান রয়েছে:

{'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]}

2
এছাড়াও, এটির জন্য একটি গ্রন্থাগার রয়েছে: github.com/ianlini/flatten-dict
Ufos

এছাড়াও দেখুন: stackoverflow.com/questions/14692690
dreftymac

উত্তর:


220

মূলত আপনি একইভাবে নেস্টেড তালিকা সমতল করতে চান, আপনাকে কেবল কী / মান দ্বারা ডিকটি পুনরুক্তকরণ, আপনার নতুন অভিধানের জন্য নতুন কী তৈরি করা এবং চূড়ান্ত পদক্ষেপে অভিধান তৈরি করার জন্য অতিরিক্ত কাজ করতে হবে।

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}

7
যদি আপনি এটি isinstanceকোনও try..exceptব্লক দিয়ে প্রতিস্থাপন করেন তবে এটি যে কোনও ম্যাপিংয়ের জন্য কাজ করবে, তা এখান থেকে উত্পন্ন না হলেও dict
বিজার্ন পোলেক্স

1
collections.MutableMappingএটিকে আরও জেনেরিক করার জন্য এটি পরীক্ষায় পরিবর্তন করেছে । তবে পাইথনের জন্য <2.6, try..exceptসম্ভবত সেরা বিকল্প।
ইমরান

5
আপনি খালি অভিধান একরকমের চ্যাপ্টা সংস্করণে সংরক্ষিত চান আপনি পরিবর্তন করতে চাইতে পারেন if isinstance(v, collections.MutableMapping):করতেif v and isinstance(v, collections.MutableMapping):
tarequeh

3
মনে রাখবেন যে new_key = parent_key + sep + k if parent_key else kধরে নেওয়া হয়েছে যে কীগুলি সর্বদা স্ট্রিং হয়, অন্যথায় এটি উত্থাপন করবে TypeError: cannot concatenate 'str' and [other] objects। তবে, আপনি কেবল kস্ট্রিং ( str(k)), বা স্ট্রিংয়ের পরিবর্তে টিপলে কী চাপিয়ে (টিউপসগুলি ডিক কীও হতে পারে) দ্বারা এটি স্থির করতে পারেন।
স্কট এইচ


65

মূল পোস্টারটি বিবেচনা করার জন্য দুটি বড় বিবেচ্য বিষয় রয়েছে:

  1. কী স্পেস ক্লোবার্বিংয়ের সমস্যা আছে? উদাহরণস্বরূপ, {'a_b':{'c':1}, 'a':{'b_c':2}}ফলাফল হবে {'a_b_c':???}। নীচের সমাধানটি জোড়ের পুনরাবৃত্তিযোগ্য ফিরিয়ে সমস্যাটি থেকে মুক্তি দেয়।
  2. কর্মক্ষমতা যদি কোনও সমস্যা হয় তবে কী-রিডুসার ফাংশনটি (যার দ্বারা আমি 'যোগ' হিসাবে উল্লেখ করেছি) পুরো কী-পাথটিতে অ্যাক্সেসের প্রয়োজন আছে, বা এটি কেবল গাছের প্রতিটি নোডে ও (1) কাজ করতে পারে? যদি আপনি বলতে সক্ষম হন joinedKey = '_'.join(*keys)তবে এটির জন্য আপনার ও (N ^ 2) ব্যয় করতে হবে। তবে আপনি যদি বলতে ইচ্ছুক হন তবে nextKey = previousKey+'_'+thisKeyএটি আপনাকে ও (এন) সময় দেয়। নীচের সমাধানটি আপনাকে উভয়ই করতে দেয় (যেহেতু আপনি কেবল সমস্ত কীগুলি একত্রিত করতে পারেন, তারপরে সেগুলি পোস্টপ্রসেস করুন)।

(পারফরম্যান্স সম্ভবত কোনও সমস্যা নয়, তবে অন্য কেউ যত্ন নেওয়ার ক্ষেত্রে আমি দ্বিতীয় পয়েন্টটি বিস্তারিতভাবে বর্ণনা করব: এটি বাস্তবায়নের ক্ষেত্রে, অনেক বিপজ্জনক পছন্দ রয়েছে you আপনি যদি এটি পুনরাবৃত্তভাবে করেন এবং ফলন এবং পুনরায় ফলন দেন, বা সমান কিছু যা স্পর্শ করে একাধিকবার নোড (যা ঘটনাক্রমে করতে বেশ সহজ), আপনি সম্ভাব্য 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 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(বাম) সাথে অপরিচিতদের জন্য একটি চিত্র রয়েছে যা অন্যথায় "ভাঁজ বাম" নামে পরিচিত। কখনও কখনও এটি কে 0 এর জায়গায় প্রাথমিক মান দিয়ে আঁকা হয় (তালিকার অংশ নয়, ফাংশনটিতে স্থান পেয়েছে)। এখানে,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)

বিক্ষোভ (যা আমি অন্যথায় ডকাস্ট্রিংয়ে রেখেছি):

>>> 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

... দীর্ঘশ্বাস ফেলো, একথা আমার দোষ বলে মনে করো না ...


[সংযোজন সংক্রান্ত সমস্যার কারণে গুরুত্বহীন historicalতিহাসিক নোট]

পাইথনের তালিকাগুলির অভিধানের অভিধান (২ স্তরের গভীর) একটি ফ্ল্যাটনের কথিত সদৃশ সম্পর্কে :

এই প্রশ্নের সমাধানটি এর মাধ্যমে কার্যকর করা যেতে পারে sorted( sum(flatten(...),[]) )। বিপরীত সম্ভব নয়: যখন এটি সত্যি যে মান এর flatten(...)একটি উচ্চ-অর্ডার সঁচায়ক ম্যাপিং দ্বারা কথিত ডুপ্লিকেট থেকে উদ্ধার করা সম্ভব, এক চাবি পুনরুদ্ধার করতে পারবেন না। (সম্পাদনা করুন: এছাড়াও এটি প্রমাণিত হয়েছে যে কথিত সদৃশ মালিকের প্রশ্নটি সম্পূর্ণ আলাদা, কারণ এটি কেবলমাত্র 2-স্তর গভীর অভিধানে ডিল করে, যদিও এই পৃষ্ঠার উত্তরগুলির মধ্যে একটি সাধারণ সমাধান দেয়))


2
এটি প্রশ্নের সাথে প্রাসঙ্গিক কিনা তা আমি নিশ্চিত নই। এই সমাধানটি অভিধানের তালিকার একটি অভিধান আইটেমকে সমতল করে না, যেমন {'ক': [{'আ': 1}, ab 'আব': 2}]}} এই ক্ষেত্রে সামঞ্জস্য করার জন্য ফ্ল্যাটটিক্ট ফাংশনটি সহজেই পরিবর্তন করা যায়।
স্টিভবাকা

55

অথবা আপনি যদি ইতিমধ্যে পান্ডা ব্যবহার করেন তবে আপনি এটির 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]}

4
অথবা কেবল
ব্লু মুন

2
লজ্জার বিষয় এটি তালিকা পরিচালনা করে না :)
রোল্যান্ট

31

আপনি যদি ব্যবহার 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 (ছাড়া _)


1
আমার জন্য যা কাজ ছিল তা ছিল from pandas.io.json._normalize import nested_to_record। এর _আগে আন্ডারস্কোরটি লক্ষ করুন normalize
আইয়াল লেভিন

2
পছন্দ করেছেন এটি পরিবর্তিত হয়েছে 0.25.x, আমি উত্তর আপডেট করেছি। :)
অ্যারন এন। ব্রক

28

এখানে এক ধরণের "ক্রিয়ামূলক", "ওয়ান-লাইনার" বাস্তবায়ন রয়েছে। এটি পুনরাবৃত্তিযোগ্য এবং শর্তাধীন অভিব্যক্তি এবং ডিক বোঝার উপর ভিত্তি করে।

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
অভিধানগুলির

@alancalvitti এটি এটিকে একটি স্ট্রিং বা অন্য কিছু যা +অপারেটর সমর্থন করে বলে ধরে নিয়েছে । অন্য যে কোনও কিছুর জন্য আপনাকে prefix + separator + kঅবজেক্টগুলি রচনা করতে উপযুক্ত ফাংশন কলের সাথে খাপ খাইয়ে নিতে হবে।
ডিভিডবাইজারো

টিপল কীগুলির সাথে প্রাসঙ্গিক আরেকটি সমস্যা। আমি আপনার পদ্ধতির উপর ভিত্তি করে কীভাবে সাধারণকরণ করব তা আলাদাভাবে পোস্ট করেছি। তবে এটি নিঞ্জাগেকোর উদাহরণটিকে সঠিকভাবে পরিচালনা করতে পারে না:{'a_b':{'c':1}, 'a':{'b_c':2}}
অ্যালানচলভিটি

2
পুনরাবৃত্তিটি ব্যবহার করে কোনও উত্তর না পেয়ে আমি উদ্বিগ্ন হয়ে পড়ছিলাম। আজকাল আমাদের যুবসমাজের সাথে কী সমস্যা?
জাকভ

কোনও ডিক যদি নালাগুলির তালিকা নীস্ট করে থাকে তবে কিছুই করে না:{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
গ্রেজলি এম

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}

আমি পাইথন 3.2 ব্যবহার করছি, অজগরটির সংস্করণটির জন্য আপডেট করুন।


আপনি সম্ভবত lkey=''ফাংশনটি কল করার পরিবর্তে আপনার ফাংশন সংজ্ঞায় এর ডিফল্ট মান নির্দিষ্ট করতে চান । এই বিষয়ে অন্যান্য উত্তর দেখুন।
একুম্যানাস

6

পাইথন 3.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}

2
পাঠযোগ্য ও কার্যক্ষম সমাধান সম্পর্কে কীভাবে? ;) আপনি কোন সংস্করণটিতে এটি পরীক্ষা করেছেন? পাইথন ৩.৪.৩ এ চেষ্টা করার সময় আমি "সিনট্যাক্স ত্রুটি" পাচ্ছি। দেখে মনে হচ্ছে যে "** সমস্ত" ব্যবহার বৈধ নয়।
ইনগো ফিশার

আমি পাইথন 3.5 থেকে কাজ করি। এটি 3.4 এর সাথে কাজ করে না জানতেন। আপনি ঠিক বলেছেন এটি খুব পঠনযোগ্য নয়। আমি উত্তর আপডেট। আশা করি এটি এখন আরও পাঠযোগ্য। :)
রোটারেটি

1
আমদানি হ্রাস অনুপস্থিত। এখনও কোডটি বুঝতে অসুবিধাটি পেয়েছেন
ইনগো ফিশার

আমি রাজী. পাইথন আসলে কার্যকরী প্রোগ্রামিংয়ের জন্য ডিজাইন করা হয়নি । তবুও আমি reduceআপনাকে অভিধানগুলি হ্রাস করার প্রয়োজনে দুর্দান্ত বলে মনে করি । আমি উত্তর আপডেট। এখন আরও কিছুটা অজগর দেখানো উচিত।
রোটারেটি

6

এটি অভিধানে সীমাবদ্ধ নয়, প্রতিটি ম্যাপিং টাইপ যা .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)

1
যদি dতা নয় dictতবে কাস্টম ম্যাপিংয়ের ধরণ যা কার্যকর করে না তবে itemsআপনার ফাংশনটি ঠিক তখনই সেখানে ব্যর্থ হবে। সুতরাং, এটি প্রতিটি ম্যাপিং প্রকারের জন্য কাজ করে না কেবল তাদের প্রয়োগ করে items()
ব্যবহারকারী 6037143

@ ইউজার 6037143 আপনি কি কখনও ম্যাপিং টাইপের মুখোমুখি হয়েছেন যা প্রয়োগ করে না items? আমি একটি দেখতে আগ্রহী হতে হবে।
ট্রে হুনার

1
@ ব্যবহারকারীর 6037143, আইটেমগুলি কার্যকর না করা হলে সংজ্ঞা অনুসারে আপনি তা করেন নি এটি ম্যাপিংয়ের কোনও প্রকার নয়।
দাউদ তাগাওহী-নেজাদ

@ দাউদটাঘাভি-নেজাদ, সাধারণ কীগুলি হ্যান্ডেল করার জন্য আপনি কী এটি পরিবর্তন করতে পারেন যেমন টিপলগুলি অভ্যন্তরীণভাবে চ্যাপ্টা করা উচিত নয়।
অ্যালানচলভিটি

5

আমার পাইথন ৩.৩ সমাধান জেনারেটর ব্যবহার করে:

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}

আপনি কি স্ট্র (টিপল সহ) ব্যতীত অন্য কোনও বৈধ কী টাইপ পরিচালনা করতে প্রসারিত করতে পারেন? স্ট্রিং কনটেনটেশনের পরিবর্তে এগুলিকে একটি টিপলে যোগ দিন।
অ্যালানচলভিটি

4

নেস্টেড অভিধানগুলি সমতল করার সহজ ফাংশন। পাইথন 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}

প্যারেন্ট কীগুলি রাখাও সহজ।


4

পুনরাবৃত্তির ব্যবহার, এটিকে সহজ এবং মানব পাঠযোগ্য:

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__পদ্ধতিটি বাস্তবায়িত করতে পারে হ্যাশযোগ্য বস্তুগুলির সাথে এটি কাজ করবে তবে অযাচিত ফলাফল পাবে।


3

এটি ইমরানের এবং রালুর উত্তর উভয়ের সাথে মিল। এটি কোনও জেনারেটর ব্যবহার করে না, পরিবর্তে একটি বন্ধের সাথে পুনরাবৃত্তি নিয়োগ করে:

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 কখনই ফিরে আসে না এবং এটি কখনই ফিরে আসবে বলে আশা করা যায় না। এটি সম্ভবত একটি subfunction বা পরিবর্তে একটি সংযুক্ত ফাংশন হিসাবে উল্লেখ করা যেতে পারে ।
একিউম্যানাস

3

দাউদের সমাধান খুব সুন্দর তবে সন্তোষজনক ফলাফল দেয় না যখন নেস্টেড ডিকটিতে ডিক্টের তালিকা থাকে তবে তার কোডটি সেই ক্ষেত্রে মানিয়ে নেওয়া যায়:

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
বোফোনটেন

2
দয়া করে isinstance(v, list)পরিবর্তে ব্যবহার করুন
দ্রুষ্কা

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

দ্রষ্টব্য: এটি চাবিতে ইতিমধ্যে উপস্থিত '_' এর জন্য অ্যাকাউন্ট করে না, অনেকটা সমতল অংশগুলির মতো।


2

এখানে মার্জিত, স্থান প্রতিস্থাপনের জন্য একটি অ্যালগরিদম। পাইথন ২.7 এবং পাইথন ৩.৫ নিয়ে পরীক্ষা করেছেন। বিভাজক হিসাবে বিন্দু অক্ষর ব্যবহার করে।

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ফাংশন সহ এই কোডটি প্রকাশ করেছি ।


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))

2
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

2
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

এটি আমাদের নেস্টেড ডিকের ভিতরে তালিকাগুলির সাথে কাজ করে, তবে কাস্টম বিভাজক বিকল্প নেই
নিখিল ভিজে

2

আমি কীভাবে স্বয়ংক্রিয়ভাবে চাবিগুলি ফ্ল্যাট করতে ইউজারডিক্টের সাবক্লাসের কথা ভাবছিলাম।

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)

ঃ উড়ে যাওয়ার উপায়ে কীগুলি যুক্ত করা যায় বা অবাক না করে স্ট্যান্ডার্ড ডিক ইনস্ট্যান্সেশন ব্যবহার করা যায় সেগুলি:

>>> 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'}

1
এফডি ['ব্যক্তি'] কে অর্পণ করা কিন্তু এর বিদ্যমান মান বজায় রাখা বেশ অবাক করা। নিয়মিত ডিক্টস কীভাবে কাজ করে তা তা নয়।
টিবিএম

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}

2
type(i).__name__=='dict'এর সাথে type(i) is dictবা সম্ভবত আরও ভাল isinstance(d, dict)(বা Mapping/ MutableMapping) প্রতিস্থাপন করা যেতে পারে ।
ক্রিশ্চিয়ান সিউপিতু

1

ডেকে.পোপিটেম () ব্যবহার করে সরাসরি নেস্টেড-তালিকার মতো পুনরাবৃত্তি:

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)}

1

ওপি যা বলেছিল ঠিক তাই নয়, প্রচুর লোকেরা এখানে আসছেন রিয়েল-ওয়ার্ল্ড নেস্টেড জেএসওএন ডেটা সমতল করার উপায়গুলির সন্ধানের জন্য যা কী-ভ্যালু জেসন অবজেক্টস এবং অ্যারে এবং জেসন অবজেক্টগুলিতে অ্যারে ইত্যাদিতে থাকতে পারে। 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}

আমার যে কাজটি করা দরকার তা অন্ড করে: আমি এগুলিতে যে কোনও জটিল জসন নিক্ষেপ করি এবং এটি আমার জন্য এটি ফ্ল্যাট করে দেয়।

Https://github.com/ScriptSmith এ সমস্ত ক্রেডিট ।


1

এই সঠিক ধরণের জিনিসটি মোকাবিলার জন্য আমি সম্প্রতি চেরিপিকার নামে একটি প্যাকেজ লিখেছিলাম যেহেতু আমাকে প্রায়শই এটি করতে হয়েছিল!

আমি মনে করি যে নিম্নলিখিত কোডটি আপনাকে ঠিক তার পরে যা দেবে:

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 এ আরও দস্তাবেজ এবং গাইডেন্স রয়েছে ।

অন্যান্য পদ্ধতিগুলি দ্রুত হতে পারে তবে এই প্যাকেজের অগ্রাধিকার হ'ল এই জাতীয় কাজগুলি সহজ করে তোলা । আপনার কাছে যদিও সমতল করার জন্য সামগ্রীর একটি বৃহত তালিকা রয়েছে, আপনি চেরিপিকারকে জিনিসগুলি গতি বাড়ানোর জন্য সমান্তরাল প্রক্রিয়াকরণ ব্যবহার করতেও বলতে পারেন।


আমি বিকল্প পদ্ধতির পছন্দ করি।
জারজিলি এম

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: /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

0

এই ফ্ল্যাটেন নেস্টেড ডিকশনারিগুলির ভিন্নতা, ম্যাক্সলেভেল এবং কাস্টম রিডুসার দিয়ে কী সংকোচন করছে

  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)

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}}

0

আমি এই পৃষ্ঠায় কয়েকটি সমাধানের চেষ্টা করেছি - যদিও সব কিছু নয় - তবে আমি চেষ্টা করেছি তারা ডিকের নেস্টেড তালিকা পরিচালনা করতে ব্যর্থ হয়েছিল।

এই মত একটি ডিক বিবেচনা করুন:

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: {}কে / ভি জুটির মতো খালি ডিক্ট রাখে না ।

  • এটি নেস্টেড টিউপসগুলিতে ফাঁকফোকর করে না


-1

কেবল ব্যবহার করুন 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='_')
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.