0

I have a list which contains data in the following format.

Data format: "1.3.4.2.1", "1.45.67.32.2", ...(the strings separated by dots 
can be of varying lengths)

This strings represent the route to the "status" node and the "last index" in the string represent the value that has to be assigned to the "@default" attribute in the status node.

I have a json of the following format.

json_tree = 
{
  "Gardens": {
    "Seaside": {
      "@loc": "porch",
      "@myID": "1.2.3",
      "Tid": "1",
      "InfoList": {
        "status": {
          "@default": "0",
          "@myID": "26"
        },
        "count": {
          "@default": "0",
          "@myID": "1"
        }
      },
      "BackYard": {
        "@loc": "backyard",
        "@myID": "75",
        "Tid": "2",
        "InfoList": {
          "status": {
            "@default": "6",
            "@myID": "32"
          },
          "count": {
            "@default": "0",
            "@myID": "2"
          }
        }
      }
    }
  }
}

In this case, my route list contains the following information.

route_list = ["1.2.3.26.4","1.2.3.75.32.2",...] # this json_tree could have many more layers in the format shown above

Note: '1.2.3.26" is the route to the "status" node in "Gardens" and "4" is the value to be assigned to the "@default" node in the "status".
Note: '1.2.3.75.32" is the route to the "status" node in "BackYard" and "2" is the value to be assigned to the "@default" node in the "status".

As of now, I have the following method. I am unable to move further from here.

for item in route_list:
   UpdateJsonTree(json_tree, item)

def UdpateJsonTree(json_tree, item):
   # I am unsure on how to parse the json tree based on the route given
   # and update the '@default' value of the status node 

Any help would be appreciated. Thanks.

Laura Smith
  • 293
  • 3
  • 13
  • Do your know how to simple acces an element? If the answer is yes iterate your information in reverse order key to key your_dict[k_n][k_n-1][...] – Wonka Aug 02 '19 at 12:18
  • @Wonka I am not sure on how to do that. May you please assist me in it? – Laura Smith Aug 02 '19 at 12:21
  • I cant find the element "1.2.3.26" in your dict, but example to acces -> json_tree["Gardens"]["Seaside"]. But Im not pretty sure to understund your problem and what you wanna do. – Wonka Aug 02 '19 at 12:26
  • The "1.2.3" is the value of the "@myID" attribute in the "Seaside" node. The "26" is the value in the "@myID" attribute in the status node. The string "1.2.3.26" represents the route to the status node based on the values of the "@myID" attribute. Once I have found the "status" node based on the "1.2.3.26", then I need to assign the "@default" attribute the value of "4". I have given examples above in the text. – Laura Smith Aug 02 '19 at 12:37
  • In `json_tree` should Seaside and BackYard be at the same level - if so you are missing a curly brace before the comma (,) before BackYard? – DaveStSomeWhere Aug 02 '19 at 12:53
  • @DaveStSomeWhere The "backYard" is a nested node in the "Seaside". ```Note: '1.2.3.75.32" is the route based on the values of the "@myID" attribute to the "status" node in "BackYard" and "2" is the value to be assigned to the "@default" node in the "status".``` – Laura Smith Aug 02 '19 at 12:58
  • 1
    Then your result will be to update the Seaside status from `0` to `4` and update BackYard from `6` to `2`. Also, you want to drive the process based on the `route_list`? – DaveStSomeWhere Aug 02 '19 at 13:15
  • @DaveStSomeWhere Exactly, that is what I want to do and the value would be 2. – Laura Smith Aug 02 '19 at 13:19
  • @DaveStSomeWhere Any update? I have tried something like this as of now. It allows me to access the "@default" value for a given @myID" value.```def get_id(d, myid): if isinstance(d, dict) and d.get('@myID') == myid: yield {'@myID':d['@oid'], 'status':d['InfoList']['status']['@default']} for i in getattr(d, 'values', lambda :[])(): yield from get_id(i, myid)``` – Laura Smith Aug 02 '19 at 13:53
  • @RomanPerekhrest Could you help me to figure this out? Thanks. – Laura Smith Aug 02 '19 at 14:04
  • @DaveStSomeWhere i am still unable to make any progress. Could you help? – Laura Smith Aug 02 '19 at 15:39
  • 1
    Still working it. Trying the [flatten](https://stackoverflow.com/questions/6027558/flatten-nested-dictionaries-compressing-keys) solution from the 179 point answer here to isolate the '@myID` occurrences, now looking into the back looping for the entire route. I'm slowly making progress. – DaveStSomeWhere Aug 02 '19 at 16:11
  • @DaveStSomeWhere ```def get_id(d, system, match): for k1, v1 in d.items(): for k,v in v1.items(): if k == system: return '{}{}.{}'.format(d['@myID'] + '.' if '@myID' in d else '', v['@myID'], v['InfoList']['status']['@myID']) else: for item in v: if (item == system and (v['@myID'] + '.' + v[item]['@myID'] + '.' + v['InfoList']['status']['@myID']) == (match)): return(v['@myID'] + '.' + v[item]['@myID'] + '.' + v['InfoList']['status']['@myID'])``` – Laura Smith Aug 02 '19 at 16:16
  • @DaveStSomeWhere thank you so much for the update. I tried sth like the one above. Still resolving the bugs in it. – Laura Smith Aug 02 '19 at 16:16
  • 1
    Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/197399/discussion-between-davestsomewhere-and-laura-smith). – DaveStSomeWhere Aug 02 '19 at 16:21

1 Answers1

1

This approach starts by building a route master that includes a list entry for each route to check against (displayed at bottom).

Once the route master is available then the route check is straight forward.

flatten is from the 179 point answer here

Please let me know if you need any clarification.

Here's the code:

import _collections_abc
import itertools
from pprint import pprint

l1 = [
    '1.2.3.26.4',
    '1.2.3.75.32.2'
]

json_tree = {
    "Gardens": {
        "Seaside": {
            "@loc": "porch",
            "@myID": "1.2.3",
            "Tid": "1",
            "InfoList": {
                "status": {
                    "@default": "0",
                    "@myID": "26"
                },
                "count": {
                    "@default": "0",
                    "@myID": "1"
                }
            },
            "BackYard": {
                "@loc": "backyard",
                "@myID": "75",
                "Tid": "2",
                "InfoList": {
                    "status": {
                        "@default": "6",
                        "@myID": "32"
                    },
                    "count": {
                        "@default": "0",
                        "@myID": "2"
                    }
                }
            }
        }
    }
}


def flatten(d, parent_key='', sep='_'):
    """Return flattened dict as list"""
    items = []
    for k, v in d.items():
        new_key = parent_key + sep + k if parent_key else k
        if isinstance(v, _collections_abc.MutableMapping):
            items.extend(flatten(v, new_key, sep=sep).items())
        else:
            items.append((new_key, v))
    return dict(items)


def add_id(path, id_value):
    """Update list of @myID paths"""
    id_list.append([path, id_value])


def update_json(path, value):
    """Update the json_data with the default value"""
    l_bracket_q = "['"
    r_bracket_q = "']"
    at_default = "['@default']"
    result = ''
    cmd = "json_tree"
    for key in path:
        cmd += l_bracket_q + key + r_bracket_q
    cmd += at_default
    cmd += ' = value'
    exec(cmd)


flat_list = flatten(json_tree)
id_list = []

#  Loop the flattened list filtering for ID's, excluding the ones in count
for k, v in [x for x in flat_list.items() if '@myID' in x[0] and 'count' not in x[0]]:
    add_id(k, v)

route_list = []

# start building the route list from the filtered id list
for id_entry in id_list:
    route_list.append(
        [[x for x in id_entry[0].split('_') if x != '@myID'],
         [y for y in id_entry[1].split('.')]])

route_master = []

#  generate the route master to include the path and the full route (as list not 1.2.3)
for id_entry in id_list:
    s1 = id_entry[0].split('_')
    tmp_route = []
    tmp_list = []
    for i in range(len(s1)):
        if s1[i] == '@myID':
            break
        tmp_list.append(s1[i])
        for rte in route_list:
            if tmp_list == rte[0]:
                tmp_route.append(rte[1])
    route_item = list(itertools.chain(*tmp_route))
    tmp_list.append(route_item)
    route_master.append(tmp_list)

#  break out the default value from the route of the main driver file
l2 = list(zip(['.'.join(x.split('.')[:-1]) for x in l1], [x.split('.')[-1] for x in l1]))

#  loop the routes to process and update when found
for route, default in l2:
    for check_it in route_master:
        if route.split('.') == check_it[-1]:
            update_json(check_it[:-1], default)

#  print results
pprint(json_tree)

results:

{'Gardens': {'Seaside': {'@loc': 'porch',
                         '@myID': '1.2.3',
                         'BackYard': {'@loc': 'backyard',
                                      '@myID': '75',
                                      'InfoList': {'count': {'@default': '0',
                                                             '@myID': '2'},
                                                   'status': {'@default': '2',
                                                              '@myID': '32'}},
                                      'Tid': '2'},
                         'InfoList': {'count': {'@default': '0', '@myID': '1'},
                                      'status': {'@default': '4',
                                                 '@myID': '26'}},
                         'Tid': '1'}}}

Route Master:

['Gardens', 'Seaside', ['1', '2', '3']]
['Gardens', 'Seaside', 'InfoList', 'status', ['1', '2', '3', '26']]
['Gardens', 'Seaside', 'BackYard', ['1', '2', '3', '75']]
['Gardens', 'Seaside', 'BackYard', 'InfoList', 'status', ['1', '2', '3', '75', '32']]
DaveStSomeWhere
  • 2,475
  • 2
  • 22
  • 19