Harry
Harry

Reputation: 13329

Semi Flatten a dictionary

Say I have this dictionary:

"pools": {
        "JP": {
            "longName": "Jackpot",
            "poolTotal": 318400,
            "shortName": "Jpot",
            "sortOrder": 9
        }
    },

How would I output this so I have it like this:

pool_JP_longname: Jackpot
pool_JP_poolTotal: 318400
etc
etc

The Nesting should not be limited to 2 or 3 levels, so it should be generic.

Or another example:

{
    "soccer": {
        "X07": {
            "date": "2013-11-22",
            "poolType": "S10",
            "code": "INT",
            "closeTime": "20:00:00",
            "poolStatus": "OP",
            "pool": {
                "1": {
                    "startRace": 1,
                    "matchs": {
                        "1": {
                            "teamA": "Ajax Cape Town",
                            "teamB": "Moroka Swallows",
                            "matchStatus": "OP"
                        },
                        "2": {
                            "teamA": "Bidvest Wits",
                            "teamB": "MP Black Aces",
                            "matchStatus": "OP"
                        }
                    }
                }
            }
        }
    }
}

Would look like this:

soccer_X07_data: "2013-11-22"
soccer_X07_poolType: "S10"
etc
soccer_X07_pool_1_matchs_1_teamA
soccer_X07_pool_1_matchs_1_teamB
etc

I had started doing it like this, but this is not correct:

def iterTool(json_data, key_string):
    for root_key, item in sorted(json_data.items(), key=itemgetter(0)):
        if type(json_data[root_key]) == dict:
            key_string += "_%s" % root_key
            if json_data[root_key].keys():
                for parent_key in json_data[root_key]:
                    if type(json_data[root_key][parent_key]) in [unicode]:
                        print "%s_%s" % (key_string, parent_key)
                        # print key_string.split("_")
                        # pass
            iterTool(json_data[root_key], key_string)

This would outout like this:

_soccer_X07_code
_soccer_X07_poolStatus
_soccer_X07_closeTime
_soccer_X07_poolType
_soccer_X07_date
_soccer_X07_pool_1_matchs_1_matchStatus
_soccer_X07_pool_1_matchs_1_teamA
_soccer_X07_pool_1_matchs_1_teamB
_soccer_X07_pool_1_matchs_1_10_matchStatus
_soccer_X07_pool_1_matchs_1_10_teamA
_soccer_X07_pool_1_matchs_1_10_teamB
_soccer_X07_pool_1_matchs_1_10_2_matchStatus
_soccer_X07_pool_1_matchs_1_10_2_teamA
_soccer_X07_pool_1_matchs_1_10_2_teamB
_soccer_X07_pool_1_matchs_1_10_2_3_matchStatus
_soccer_X07_pool_1_matchs_1_10_2_3_teamA
_soccer_X07_pool_1_matchs_1_10_2_3_teamB
_soccer_X07_pool_1_matchs_1_10_2_3_4_matchStatus
_soccer_X07_pool_1_matchs_1_10_2_3_4_teamA
_soccer_X07_pool_1_matchs_1_10_2_3_4_teamB
_soccer_X07_pool_1_matchs_1_10_2_3_4_5_matchStatus
_soccer_X07_pool_1_matchs_1_10_2_3_4_5_teamA
...

Now just another curveball..

Let say the dict looks like this:

{
    "CAP": {
        "countryName": "ZAF",
        "displayName": "AN"
    },
    "SPA": {
        "countryName": "AUs",
        "displayName": "AG"
    }
}

Then it wont make sense to flatten it but rather:

GENERIC_KEY:CAP, countryName:ZAF, displayName:AN

How would you detect this?

Upvotes: 0

Views: 95

Answers (3)

jonrsharpe
jonrsharpe

Reputation: 122024

This will recursively flatten your dictionaries:

def flatten_dict(dct, output=None, prefix=None):
    if output is None:
        output = {}
    if prefix is None:
        prefix = []
    for key in dct:
        if isinstance(dct[key], dict):
            flatten_dict(dct[key], output, prefix + [key])
        else:
            output["_".join(prefix + [key])] = dct[key]
    return output

For your second example, I get:

{'soccer_X07_pool_1_matchs_2_teamA': 'Bidvest Wits', 
 'soccer_X07_pool_1_matchs_2_teamB': 'MP Black Aces',
 'soccer_X07_pool_1_matchs_1_matchStatus': 'OP', 
 ...}

Upvotes: 2

sloth
sloth

Reputation: 101052

A simple solution could look like this:

d = { ... }

def flatten(dic, stack=None):
    if not stack: stack = []
    for key,value in dic.iteritems():
        new_stack = stack[:] + [key]
        if isinstance(value, dict): 
            for result in flatten(value, new_stack):
                yield result
        else: 
            yield new_stack, value

# just print it:           
for stack, value in flatten(d):
    print '{}: {}'.format('_'.join(stack), value)

# create a new dict:
new_d = {'_'.join(stack): value for stack, value in flatten(d)}

Upvotes: 1

James Mills
James Mills

Reputation: 19030

This is a basic recursion problem (or one that can be solved with recursion):

Here is a contrived example that satisfies your first example (more or less):

d = {
    "pools": {
        "JP": {
            "longName": "Jackpot",
            "poolTotal": 318400,
            "shortName": "Jpot",
            "sortOrder": 9
        }
    }
}


def flattendict(d):
    for k, v in d.items():
        if isinstance(v, dict):
            for x in flattendict(v):
                yield "{}_{}".format(k, x)
        else:
            yield "{}_{}".format(k, v)


for item in flattendict(d):
    print item

NB: I've left several issues for you to problem solve and investigate.

Upvotes: 0

Related Questions