user489176
user489176

Reputation: 113

Creating a hierarchy path from JSON

Given the JSON below, what would be the best way to create a hierarchical list of "name" for a given "id"? There could be any number of sections in the hierarchy.

For example, providing id "156" would return "Add Storage Devices, Guided Configuration, Configuration"

I've been looking into using iteritems(), but could do with some help.

 {
    "result": true,
    "sections": [
        {
            "depth": 0,
            "display_order": 1,
            "id": 154,
            "name": "Configuration",
            "parent_id": null,
            "suite_id": 5
        },
        {
            "depth": 1,
            "display_order": 2,
            "id": 155,
            "name": "Guided Configuration",
            "parent_id": 154,
            "suite_id": 5
        },
        {
            "depth": 2,
            "display_order": 3,
            "id": 156,
            "name": "Add Storage Devices",
            "parent_id": 155,
            "suite_id": 5
        },
        {
            "depth": 0,
            "display_order": 4,
            "id": 160,
            "name": "NEW",
            "parent_id": null,
            "suite_id": 5
        },
        {
            "depth": 1,
            "display_order": 5,
            "id": 161,
            "name": "NEWS",
            "parent_id": 160,
            "suite_id": 5
        }
    ]
}

Upvotes: 1

Views: 733

Answers (5)

tailor_raj
tailor_raj

Reputation: 1057

you can also do like this. But for this your input should be like. Replace null with None and true with True in your input dictionary.

def filtering(d,id_n):
    names = []        
    while id_n:
        id_n,name=[(sec['parent_id'],sec['name']) for sec in d['sections'] if sec['id'] == id_n][0]
        names.append(name)
    return names

d = {
"result": True,    #making 'true' with 'True'
"sections": [
    {
        "depth": 0,
        "display_order": 1,
        "id": 154,
        "name": "Configuration",
        "parent_id": None,
        "suite_id": 5
    },
    {
        "depth": 1,
        "display_order": 2,
        "id": 155,
        "name": "Guided Configuration",
        "parent_id": 154,
        "suite_id": 5
    },
    {
        "depth": 2,
        "display_order": 3,
        "id": 156,
        "name": "Add Storage Devices",
        "parent_id": 155,
        "suite_id": 5
    },
    {
        "depth": 0,
        "display_order": 4,
        "id": 160,
        "name": "NEW",
        "parent_id": None,
        "suite_id": 5
    },
    {
        "depth": 1,
        "display_order": 5,
        "id": 161,
        "name": "NEWS",
        "parent_id": 160,
        "suite_id": 5
    }
]

}

testing the code with given inputs:-

id_n = 156
>>> filtering(d,id_n)
['Add Storage Devices', 'Guided Configuration', 'Configuration']

Upvotes: 0

Bakuriu
Bakuriu

Reputation: 101989

I believe you want something like:

def get_name_for_id(id_num, sections):
    cur_depth = -1
    texts = []
    for elem in sections:
        if elem['depth'] < cur_depth:
            del texts[:]
        elif elem['depth'] == cur_depth:
            texts.pop()
        texts.append(elem['name'])
        cur_depth = elem['depth']
        if elem['id'] == id_num:
            return ', '.join(reversed(texts))

With your data it returns:

In [11]: get_name_for_id(156, data['sections'])
Out[11]: 'Add Storage Devices, Guided Configuration, Configuration'

Also it takes into account the hierarchy based on depth, thus if in your data the id 156 refers to depth = 0 the result is:

In [16]: get_name_for_id(156, data['sections'])
Out[16]: 'Add Storage Devices'

If the depth of the id 156 was 1 then the value returned is:

In [22]: get_name_for_id(156, data['sections'])
Out[22]: 'Add Storage Devices, Configuration'

Basically it considers the trees:

depth 156 = 0 depth 156 = 1 depth 156 = 2

 154  156          154                 154
  |                 |                   |
  |                / \                 155
 155             155 156                |
                                       156

And it returns the concatenation of the names in the path from the 156 to the root of the tree.

Upvotes: 0

schesis
schesis

Reputation: 59168

Here's one approach:

def get_path(data, section_id):
    path = []
    while section_id is not None:
        section = next(s for s in data["sections"] if s["id"] == section_id)
        path.append(section["name"])
        section_id = section["parent_id"]
    return ", ".join(path)

... which assumes that data is the result of json.loads(json_text) or similar, and section_id is an int (which is what you've got for ids in that example JSON).

For your example usage:

>>> get_path(data, 156)
u'Add Storage Devices, Guided Configuration, Configuration'

Upvotes: 2

poke
poke

Reputation: 388023

This is probably what you want:

>>> sections = data['sections']
>>> lookup = {section['id']: section for section in sections}
>>> lookup[None] = {}
>>> for section in sections:
        parent = lookup[section['parent_id']]
        if 'childs' not in parent:
            parent['childs'] = []
        parent['childs'].append(section)

>>> def printRecurse (section, indent = 0):
        if 'childs' in section:
            section['childs'].sort(lambda x: x['display_order'])
            for child in section['childs']:
                print('{}{}: {}'.format('  ' * indent, child['id'], child['name']))
                printRecurse(child, indent + 1)

>>> printRecurse(lookup[None])
154: Configuration
  155: Guided Configuration
    156: Add Storage Devices
160: NEW
  161: NEWS

Upvotes: 0

zrathen
zrathen

Reputation: 229

Probably the simplest way is to create a dictionary mapping the IDs to names. For example:

name_by_id = {}
data = json.loads(the_json_string)
for section in data['sections']:
    name_by_id[section['id']] = section['name']

or using dict comprehensions:

name_by_id = {section['id']: section['name'] for section in data['sections']}

then you can get specific element:

>>> name_by_id[156]
... 'Add Storage Devices'

or get all IDs:

>>> name_by_id.keys()
... [160, 161, 154, 155, 156]

Upvotes: 0

Related Questions