psagrera psagrera - 4 months ago 5
Python Question

substract values of two nested dictionary python

I'd need to get delta of two nested dictionary:

I'm using a function like that to get a nested dictionary

def _get_data(self):
duplicates = defaultdict(list) # to append tuples into a dictionary
counter_dict = AutoVivification()
vpls_dict = AutoVivification()

fpcs = self._get_slot_fpcs_online()
pattern = "GOT:\s+(\d+).*([0-9A-F]{2,2}\:[0-9A-F]{1,2}\:[0-9A-F]{1,2}\:[0-9A-F]{1,2}\:[0-9A-F]{1,2}\:[0-9A-F]{1,2})\s+\d{4}\s+(\d\s+\d).*(\d\s+\d+/\d+)"
regex = re.compile(pattern,re.IGNORECASE)

for i in fpcs:
if i == '11':
for pfe in range(2):
cmd = self._conn.rpc.request_pfe_execute(target='fpc' + str(i),command='show l2metro '+str(pfe)+' mac hw')
cmd_str = etree.tostring(cmd)
for x in regex.findall(cmd_str):
if x[2] =='0 0' and x[3] != '7 255/255':
duplicates[i].append(x)
else:
cmd = self._conn.rpc.request_pfe_execute(target='fpc' + str(i),command='show l2metro 0 mac hw')
cmd_str = etree.tostring(cmd)
for x in regex.findall(cmd_str):
if x[2] =='0 0' and x[3] != '7 255/255':
duplicates[i].append(x)

for k,v in duplicates.iteritems():
for j in v:
cmd_vpls = self._conn.rpc.get_l2_learning_routing_instances()
vpls_instance = ''.join(cmd_vpls.xpath("//l2ald-rtb-entry[l2rtb-id=" + '"' + str(j[0]) + '"'"]/l2rtb-name//text()")[0])
vpls_dict[k][j[1]][j[3]][j[0]][vpls_instance] = self._conn.cli('show configuration routing-instances '+ vpls_instance + ' forwarding-options family vpls filter',warning=False).split('\n')[1].replace('input','').replace(';','')
counter_cmd = self._conn.rpc.get_firewall_filter_information(filtername=str(vpls_dict[k][j[1]][j[3]][j[0]][vpls_instance]).strip())
counter_dict[k][j[1]][j[3]][vpls_instance][vpls_dict[k][j[1]][j[3]][j[0]][vpls_instance].strip()] = ''.join(counter_cmd.xpath('./filter-information/policer/packet-count//text()')).replace('\n','')
return counter_dict


counter_dict result look likes:

{'10': {'00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '91304'}}},
'00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '1585097'}}},
'00:1b:c0:f2:f4:fa': {'0 255/255': {'156420': {'CDALJ1/08903762011': '0'},
'166980': {'CDALJ1/19369922011': '0'}}},
'88:e0:f3:61:d8:01': {'0 255/255': {'182099': {'CDALJ1/11274452012': '0'}}},
'ec:13:db:0a:95:01': {'0 255/255': {'182099': {'CDALJ1/11274452012': '0'}}}},

'11': {'00:00:0c:07:ac:75': {'0 255/255': {'232173': {'CDALJ1/14100001093242': '0'}}},
'00:00:0c:07:ac:f5': {'0 255/255': {'293667': {'CDALJ1/14100001095054': '2723092'}}},
'00:00:0c:07:ac:f6': {'0 255/255': {'298967': {'CDALJ1/14100001095106': '0'}}},
'00:00:0c:07:ac:f7': {'0 255/255': {'298969': {'CDALJ1/14100001095107': '0'}}},
'00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '91304'}}}

[......]

I'm trying to get a delta of inner value keeping dictionary structure :

mac_dict1 = _get_data()

{'10': {'00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '91304'}}},
'00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '1585097'}}}

sleep5

mac_dict2 = _get_data()

{'10': {'00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '91310'}}},
'00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '1585100'}}}

result = get_diff(mac_dict1,mac_dict2)

result should be provide a result like that:

{'10': {'00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '6'}}},
'00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '3'}}}

Could you provide me any hint or tip about how to do that (not the code)?

Thanks

Answer

You could do that with simple recursive function that will turn str values to int and subtract or recurse in case of dict:

def subtract(x, y):
    if isinstance(x, dict) and isinstance(y, dict):
        return {key: subtract(x[key], y[key]) for key in x if key in y}
    else:
        return str(int(x) - int(y))

Running it with given input:

d1 = {
    '10': {
        '00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '91304'}}},
        '00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '1585097'}}}
    }
}
d2 = {
    '10': {
        '00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '91310'}}},
        '00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '1585100'}}}
    }
}

print subtract(d2, d1)

Formatted output:

{
    '10': {
        '00:07:72:9d:dc:4c': {'0 255/255': {'128379': {'CDALJ1/17223002010': '6'}}}, 
        '00:0f:bb:fa:25:fd': {'0 255/255': {'232367': {'CDALJ1/14100001093228': '3'}}}
    }
}

Note that the example code fails if your input contains any other types of values than dict and str that should be interpreted as int.