Nested Dictionary for loop - python

I'm new to programming. I'm trying to figure out how to subtract 'budgeted' from 'actual' and then update the value to 'variance' using a nested for loop. However, I've read that it isn't the best practice to change a dictionary while iterating. So far, I've been stumped on how to proceed.
for i in properties:
for j in properties[i]:
if j == "actual":
sum = properties[i][j]
print('\nActual:' , sum)
if j == "budgeted":
sum_two = properties[i][j]
print('Budgeted:' , sum_two)
diff = sum_two - sum
print('Variance:', diff)
default_value = 0
properties = {587: {'prop_name': 'Collington'}, 'rental_income': {'apartment_rent': '5120-0000', 'resident_assistance': '5121-0000', 'gain_loss': '5120-0000'}, 51200000: {'actual': 29620, 'budgeted': 30509, 'variance': default_value}, 51210000: {'actual': 25620, 'budgeted': 40509, 'variance': default_value}, ............

just iterate through the dictionary and check if in the inner dictionary, if actual, variance and budgeted exists or not, if yes then modify the variance value
for k, v in properties.items():
if (('actual' in v.keys()) and ('variance' in v.keys()) and ('budgeted' in v.keys())):
properties[k]['variance'] = properties[k]['actual']-properties[k]['budgeted']

There is nothing wrong with modifying the values inside a dictionary while iterating. The only thing that is not recommend is modifying the dictionary itself, that is adding/removing elements.

Try something like:
for i in properties:
properties[i]['variance'] = properties[i]['budgeted'] - properties[i]['actual']
If you aren't sure that bugeted and actual exist in the dictionaries, you should catch the KeyError and handle approprately:
for i in properties:
try:
properties[i]['variance'] = properties[i]['budgeted'] - properties[i]['actual']
except KeyError:
properties[i]['variance'] = -1 # Set to some special value or just pass

Your data is in a strange format, I always try to group like objects together in dictionaries rather than have metadata and "lists" of items in the same level of a dictionary. This will work for you though:
for prop in properties:
p = properties[prop]
if 'actual' or 'budgeted' in p.keys():
# get() wont error if not found, also default to 0 if not found
p['variance'] = p.get('budgeted', 0) - p.get('actual', 0)
import json
print(json.dumps(properties, indent=4))
Output:
{
"587": {
"prop_name": "Collington"
},
"rental_income": {
"apartment_rent": "5120-0000",
"resident_assistance": "5121-0000",
"gain_loss": "5120-0000"
},
"51200000": {
"actual": 29620,
"budgeted": 30509,
"variance": 889
},
"51210000": {
"actual": 25620,
"budgeted": 40509,
"variance": 14889
}
}

sum = None
sum_two = None
for i in properties:
for j in i:
if j=="actual":
sum = properties [i]["actual"]
print('\nActual:' , sum)
if j == "budgeted":
sum_two = properties[i]["budgeted"]
print('Budgeted:' , sum_two)
diff = sum_two - sum
print('Variance:', diff)
I didn't get what mean exactly, but this should work.

Related

Python dictionaries shown as unequal despite no found differences

I'm comparing 2 dictionaries in Python, loadedAgreement and latestDBAgreement. I know from this thread that a simple == or != can be used for dictionary comparison.
My dictionaries come out as unequal, and to get the diffs, I used the approach recommended here:
value = { k : second_dict[k] for k in set(second_dict) - set(first_dict) }
from both sides. But when I do this from both sides, both results are empty {}. So why are the dictionaries still unequal?
diffvalues1 = { k : latestDBAgreement[k] for k in set(latestDBAgreement) - set(loadedAgreement) }
diffvalues2 = { k : loadedAgreement[k] for k in set(loadedAgreement) - set(latestDBAgreement) }
As you can see in the debugger, the code dropped into the != section, but both diffs are empty.
Dicts can also differ in values. To see which, you can do something like this:
{
k: (v, latestDBAgreement[k])
for k, v in loadedAgreement.items()
if v != latestDBAgreement[k]}
(This of course assumes that the keys are the same, so it doesn't generalize.)

Most efficient method to find values in nested dict

Let's say I have the dict below
{
"1": {
"rarity": 1
},
"2": {
"rarity": 2
}
}
and I want to find the amount of times rarity is 1. Instead of doing
count = 0
for x in dict.values():
if x['rarity'] == 1:
count += 1
return count
Is there a better way to do this? I found this post about using map, but I'm not sure how to get it to work with a nested dictionary.
You can write it much shorter:
count = sum(v["rarity"] == 1 for v in d.values())
from rioV8's comment, with inspiration from Mateen Ulhaq's comment
You can use the following helper function with the map.
def functionHelper(value):
if value["rarity"] == 1:
return 1
return 0
sum(map(functionHelper, d.values()))
or you can use one of these:
sum(map((1).__eq__, [v["rarity"] for v in d.values()]))
sum(v["rarity"] == 1 for v in d.values())

Python: append a list from a dictionary with conditions

I have a dictionary from which I want to filter out and append the results in another dictionary. The condition is if the difference between two first elements (for example 31 - 30 = 1) in the dictionary is smaller than 5 then add the associated second element of the dictionary and append it in a new dictionary else keep the same first element with the associated second element.
a = {"20" : "1.5", "30" : "2.0", "31" : "1.0", "40" : "1", "50" : "1.5"}
listb = []
listc = []
newdict = {}
for key in a:
b = key
c = a[key]
listb.append(b)
listc.append(c)
for i in range(len(listb)):
low = listb[i]
high = listb[i+1]
diff = int(high) - int(low)
# print(low)
if (diff > 5):
num = listc[i]
# print(num)
num_a = listb[i]
# print(num_a)
newdict[[num_a][i]] = num
print((newdict))
else:
num = listc[i] + listc[i+1]
print(num)
num_a = listb[i+1]
print(num_a)
newdict[[num_a][i]] = num
print(newdict)
The output of this should look something like
a = {"20" : "1.5", "31" : "3.0", "40" : "1", "50" : "1.5"}
Since you are comparing each element with the one 'before' or 'after' it, you want to use an ordered data structure. Since dictionaries are only 'insertion ordered', you cannot reliably check the first item with the one right after. So, you might want to use a list of tuples. I'm not quite sure what you are trying to do, but I tried to interpret it with this code. I hope this helps :)
# Creating a as a list of tuples so that they are ordered
a = [(20, 1.5), (30, 2.0), (31, 1.0), (40, 1), (50, 1.5)]
new_list = []
# you looped through len(a), but you should loop through len(a) - 1 so that you don't get an index error
for i in range(len(a) - 1):
# The first element of each tuple
low_key = a[i][0]
high_key = a[i+1][0]
if high_key - low_key < 5:
sum = a[i+1][1] + a[i][1]
new_tuple = (high_key, sum)
new_list.append(new_tuple)
else:
new_list.append((low_key, a[i][1]))
# need to check if last element, bc only looping through len(a) - 1
if i == len(a) - 1:
new_list.append((high_key, a[i+1][1]))
print(new_list)
If the only other answer requires the use of Pandas then I feel compelled to offer an alternative (I hate Pandas).
This should give what you describe. I'm not currently able to test though.
a = {"20" : "1.5", "30" : "2.0", "31" : "1.0", "40" : "1", "50" : "1.5"}
# your listb and listc are just a.keys() and a.values(). So I'm going to delete all of this listb listc setup stuff.
newdict = {}
skip = False # This is a pretty brute force way to just check whether we've already accounted for the "next" value. Otherwise you will double count.
for i in range(len(a.keys())):
if skip:
skip = False
continue
low = a.keys()[i]
high = a.keys()[i+1]
diff = abs(int(high) - int(low)) # If "diff" is actually meant to be a diff, then we need to use abs
if diff > 5:
newdict[a.keys()[i]] = a.values()[i]
else:
newdict[a.keys()[i]] = a.values()[i] + a.values()[i+1]
skip = True
print(newdict)
Please note that if you have several keys in a row that are all < 5 apart, this might not behave as expected. It also isn't clear from the description what you would actually want in a case where the keys were, eg, 40, 44, 48 (group 40 and 44 or group all 3 numbers?). But based on what you describe the above implements it.
One way to do this is to convert first into Pandas dataframe and do your calculations there then convert it back to dictionary?
d = {"20" : "1.5", "30" : "2.0", "31" : "1.0", "40" : "1", "50" : "1.5"}
df = pd.Series(d)
df = df.reset_index().astype(float)
df['id']= df['index'].diff().shift(-1).fillna(10).values
df = df[df['id']>5]
df = df.set_index(['index'])
df = df.drop('id', axis=1)
df.to_dict()
{0: {20.0: 1.5, 31.0: 1.0, 40.0: 1.0, 50.0: 1.5}}
I'm not really clear on what you're trying to do, but I think with a couple of comments, you might be able to fix your code to achieve your objective, even if I don't fully understand what that objective is.
A dict is inherently unsorted, but I believe your algorithm inherently requires that the keys be in increasing order.
I'd change the second and third line to:
listb = sorted(a.keys())
listc = [a[k] for k in listb]
Next you probably want to loop to len(listb) - 1. Otherwise listb[I + 1] will be out of bounds. Maybe you could check out the enumerate function, but then you'd need to check for being on the last iteration, and handle accordingly.
Finally, you could use some better variable names. a, listb, and listc doesn't really convey much meaning. Even a, a_keys, and a_values would have been easier to follow, but a better description of what a represents would be better again.

iterate through a dictionary inside a dictionary in python

So let's say i have
dictionary = {
'Moe': {
'name':'moe',
'age':24,
'married':False
},
'Jon': {
'name':'jon',
'age':22,
'married':False
},
'andrew':
{'name':'andrew',
'age':27,
'married':True
}
}
Suppose that i want to iterate through the dictionary to find out how many person is married in this dictionary, how can i do it?
You could use the following generator comprehension to lookup married in the inner dictionaries setting the default value to 0, and take the sum:
sum(i.get('married', 0) for i in dictionary.values())
#1
result = 0
for x in dictionary:
if dictionary[x]['married'] == True:
result += 1
print(result)
One way of doing it is like this:
n_married = 0
for key, item in d.items():
name, age, married = item
n_married += 1 if married else 0
print(n_married)
You need:
count = 0
for v in dictionary.values():
if v['married']:
count += 1
print(count)

Finding the max depth of a set in a dictionary

I have a dictionary where the key is a string and the values of the key are a set of strings that also contain the key (word chaining). I'm having trouble finding the max depth of a graph, which would be the set with the most elements in the dictionary, and I'm try print out that max graph as well.
Right now my code prints:
{'DOG': [],
'HIPPOPOTIMUS': [],
'POT': ['SUPERPOT', 'HIPPOPOTIMUS'],
'SUPERPOT': []}
1
Where 1 is my maximum dictionary depth. I was expecting the depth to be two, but there appears to be only 1 layer to the graph of 'POT'
How can I find the maximum value set from the set of keys in a dictionary?
import pprint
def dict_depth(d, depth=0):
if not isinstance(d, dict) or not d:
return depth
print max(dict_depth(v, depth+1) for k, v in d.iteritems())
def main():
for keyCheck in wordDict:
for keyCompare in wordDict:
if keyCheck in keyCompare:
if keyCheck != keyCompare:
wordDict[keyCheck].append(keyCompare)
if __name__ == "__main__":
#load the words into a dictionary
wordDict = dict((x.strip(), []) for x in open("testwordlist.txt"))
main()
pprint.pprint (wordDict)
dict_depth(wordDict)
testwordlist.txt:
POT
SUPERPOT
HIPPOPOTIMUS
DOG
The "depth" of a dictionary will naturally be 1 plus the maximum depth of its entries. You've defined the depth of a non-dictionary to be zero. Since your top-level dictionary doesn't contain any dictionaries of its own, the depth of your dictionary is clearly 1. Your function reports that value correctly.
However, your function isn't written expecting the data format you're providing it. We can easily come up with inputs where the depth of substring chains is more than just one. For example:
DOG
DOGMA
DOGMATIC
DOGHOUSE
POT
Output of your current script:
{'DOG': ['DOGMATIC', 'DOGMA', 'DOGHOUSE'],
'DOGHOUSE': [],
'DOGMA': ['DOGMATIC'],
'DOGMATIC': [],
'POT': []}
1
I think you want to get 2 for that input because the longest substring chain is DOG → DOGMA → DOGMATIC, which contains two hops.
To get the depth of a dictionary as you've structured it, you want to calculate the chain length for each word. That's 1 plus the maximum chain length of each of its substrings, which gives us the following two functions:
def word_chain_length(d, w):
if len(d[w]) == 0:
return 0
return 1 + max(word_chain_length(d, ww) for ww in d[w])
def dict_depth(d):
print(max(word_chain_length(d, w) for w in d))
The word_chain_length function given here isn't particularly efficient. It may end up calculating the lengths of the same chain multiple times if a string is a substring of many words. Dynamic programming is a simple way to mitigate that, which I'll leave as an exercise.
Sorry my examples wont be in python because my python is rusty but you should get the idea.
Lets say this is a binary tree:
(written in c++)
int depth(TreeNode* root){
if(!root) return 0;
return 1+max(depth(root->left), depth(root->right));
}
Simple. Now lets expand this too more then just a left and right.
(golang code)
func depthfunc(Dic dic) (int){
if dic == nil {
return 0
}
level := make([]int,0)
for key, anotherDic := range dic{
depth := 1
if ok := anotherDic.(Dic); ok { // check if it does down further
depth = 1 + depthfunc(anotherDic)
}
level = append(level, depth)
}
//find max
max := 0
for _, value := range level{
if value > max {
max = value
}
}
return max
}
The idea is that you just go down each dictionary until there is no more dictionaries to go down adding 1 to each level you traverse.

Categories

Resources