I have a list of dictionaries and need to iterate through them and check for the keys that exist already. I have implemented a python code to manually calculate a score as below. In my code, I'm manually combining keys from previous dictionaries in each iteration. Iteration will start from dict11.
How can I change this code to automatically iterate through a dynamic number of dictionaries and in each iteration how can I combine the keys dynamically?
dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}
exist_score = 0
for key in dict11.keys() & dict10.keys():
exist_score += dict11[key]
for key in dict12.keys() & set(dict11.keys()).union(set(dict10.keys())):
exist_score += dict12[key]
for key in dict13.keys() & set(dict12.keys()).union(set(dict11.keys()).union(set(dict10.keys()))):
exist_score += dict13[key]
print(exist_score)
First you need to turn this into something that can be put into a loop. The same thing has to happen with dict11, dict12 and dict13:
# same definition for dict10, dict11, dict12, dict13
exist_score = 0
seen_keys = set(dict10.keys())
for key in dict11.keys():
if key in seen_keys:
exist_score += dict11[key]
seen_keys.update(dict11.keys())
for key in dict12.keys():
if key in seen_keys:
exist_score += dict12[key]
seen_keys.update(dict12.keys())
for key in dict13.keys():
if key in seen_keys:
exist_score += dict13[key]
seen_keys.update(dict13.keys())
This should do the same thing as your script. Now you can put that into a loop …
# same definition for dict10, dict11, dict12, dict13
exist_score = 0
seen_keys = set(dict10.keys())
other_dicts = [dict11, dict12, dict13]
for d in other_dicts:
for key in d.keys():
if key in seen_keys:
exist_score += d[key]
seen_keys.update(d.keys())
It makes the most sense to keep dicts in a list themselves. Putting this logic into a function is also a no-brainer.
dicts = [
{'A': 1, 'C': 2},
{'B': 3, 'C': 4},
{'A': 5, 'E': 6, 'F': 7},
{'G': 8, 'E': 9}
]
def score_dicts(dicts):
score = 0
all_keys = set()
for d in dicts:
keys = d.keys()
for key in keys & all_keys:
score += d[key]
all_keys.update(keys)
return score
exist_score = score_dicts(dicts)
If you need to update the score periodically (one dict at a time), you can maintain the state in either a class or a closure.
Class:
class DictScorer():
def __init__(self):
self.exist_score = 0
self.all_keys = set()
def score(self, d):
keys = d.keys()
for key in keys & self.all_keys:
self.exist_score += d[key]
self.all_keys.update(keys)
return self.exist_score
dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}
scorer = DictScorer()
exist_score = scorer.score(dict10)
print(exist_score)
exist_score = scorer.score(dict11)
print(exist_score)
exist_score = scorer.score(dict12)
print(exist_score)
exist_score = scorer.score(dict13)
print(exist_score)
Closure:
# returns a scorer which can
# be used incrementally
def create_dict_scorer():
score = 0
all_keys = set()
def dict_scorer(d):
nonlocal score
keys = d.keys()
for key in keys & all_keys:
score += d[key]
all_keys.update(keys)
return score
return dict_scorer
dict10 = {'A': 1, 'C': 2}
dict11 = {'B': 3, 'C': 4}
dict12 = {'A': 5, 'E': 6, 'F': 7}
dict13 = {'G': 8, 'E': 9}
scorer = create_dict_scorer()
exist_score = scorer(dict10)
print(exist_score)
exist_score = scorer(dict11)
print(exist_score)
exist_score = scorer(dict12)
print(exist_score)
exist_score = scorer(dict13)
print(exist_score)
Short magic with slicing and set operations:
dicts = [dict10, dict11, dict12, dict13]
exist_score = 0
for i, d in enumerate(dicts[:0:-1]):
offset = -(i - 2)
exist_score += sum(d[k] for k in d.keys() & set().union(*dicts[offset::-1]))
print(exist_score)
dicts[:0:-1] - slice of dictionaries in reversed order excluding the 1st one
-(i - 2) - negative offset to get consecutive "backward" slices for further set unions
The output (the same as in your initial approach):
18
Related
all the contents must be inserted from the input
graph={
'A':{'B':3,'C':4},
'B':{'A':3,'C':5},
'C':{'B':5,'D':'1'},
'D':{'C':1},
}
Take the input in JSON format and then you can easily use json.loads to convert it to a dictionary.
>>> import json
>>> graph = json.loads(input("Enter graph as JSON: "))
Enter graph as JSON: {"A":{"B":3,"C":4},"B":{"A":3,"C":5},"C":{"B":5,"D":"1"},"D":{"C":1}}
>>> import pprint
>>> pprint.pprint(graph)
{'A': {'B': 3, 'C': 4},
'B': {'A': 3, 'C': 5},
'C': {'B': 5, 'D': '1'},
'D': {'C': 1}}
record_to_insert = int(input("num of record to insert :: "))
dic = {}
for i in range(record_to_insert):
print("")
key_name = input("parent key name :: ")
dic[key_name] = {}
print("Enter how many number of key,value pairs you want for key :: ",
key_name)
num_of_child_keyvalues_to_insert = int(input(""))
for k in range(num_of_child_keyvalues_to_insert):
key = input("child key name :: ")
value = input("Value name :: ")
dic[key_name][key] = value
print(dic)
def make_dict():
d = {}
while True:
key = input("Enter the key of dict or stay blank to finish adding key to this level of dict: ")
if key == "": # just stay blank to out from func
break
ch = input(f"Do you wanna to create nested dict for the key{key}? [y / <Press Enter for No>] ")
if ch == "y":
value = make_dict() # use recursion
else:
value = input(f"Enter the value for the key {key}: ")
d[key] = value
return d
print(make_dict())
link to screenshot
problem: create a recursive function that given an input key, would return the amount of basic components to build the given input key.
EX 1) input = "Engine"
output = Engine ==> metal: 3, rubber: 2
EX 2) input = "metal"
output = metal ==> metal: 1
EX 3) input = "piston"
output = piston ==> metal: 1, rubber: 1
car= {
"Engine" : ["pistons", "timing belt", "metal" ,"metal"],
"Pistons" : ["Metal", "rubber"],
"timing belt" : ["rubber"],
"metal" : [],
"rubber" : []
}
my code has different variable names and key name, but it's the same idea
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D','E','F'],
'D': [],
'E': ['B','D'],
'F': []
}
#above here its user input
counter_dictio = {
'A': [],
'B': [],
'C': [],
'D': [],
'E': [],
'F': []
}
def desamble(key, dictionary):
#check if array is empty
#ccounter +=1
if (len(dictionary[key])) == 0:
counter_dictio[key].append(key)
#if array is populated
#enter to this array
#desample(i, dictionary)
else:
for i in dictionary[key]:
desamble(i, dictionary)
key = "A"
desamble(key, parts)
One way to go is:
from collections import Counter
car= {
"engine": ["pistons", "timing belt", "metal", "metal"],
"pistons": ["metal", "rubber"],
"timing belt": ["rubber"],
"metal": [],
"rubber": []
}
def ingredients(key, dct):
if dct[key] == []:
yield key
else:
for sub_part in dct[key]:
yield from ingredients(sub_part, dct)
print(*ingredients('engine', car)) # metal rubber rubber metal metal
print(Counter(ingredients('engine', car))) # Counter({'metal': 3, 'rubber': 2})
ingredients makes a generator of ingredients, so you can use Counter to count them.
Here is code that will group by your components
from collections import defaultdict
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D', 'E', 'F'],
'D': [],
'E': ['B', 'D'],
'F': []
}
result = defaultdict(dict)
for k, v in parts.items():
row = result[k] # used to create empty dict on initial empty list
for item in v:
if row.get(item) is None:
row[item] = 1
else:
row[item] += 1
This will result in following dict
{'A': {'B': 2, 'C': 1}, 'B': {}, 'C': {'D': 1, 'E': 1, 'F': 1}, 'D': {}, 'E': {'B': 1, 'D': 1}, 'F': {}}
another solution without using recursively, and for a predetermined list would be:
#==== user input====
key = "E"
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D','E','F'],
'D': [],
'E': ['B','D'],
'F': []
}
#====end user input=====
#Function for the predetermined dictionary
def desamble(key, dictionary):
if key == "B" or key == "D" or key == "F":
print(key + "==> " + key + ": 1")
elif key == "E":
print(key + "==> " + "B: 1, D: 1" )
elif key == "C":
print(key + "==> " + "B: 1, D: 2, F: 1" )
elif key == "A":
print(key + "==> " + "B: 3, D: 2, F: 1" )
else:
print("Key " + key + " is not defined in dictionary")
#====end====
desamble(key, parts)
Another recursive way to solve this problem, adding a solution for the problem of circularity, meaning that in case that the parts call to eachother.
EX)
dictionary = {
"A": "B",
"B": "A"
}
from typing iport Counter
def func(key, dictionary, current=None):
if not current:
current = set() # could also be a list
if key in current:
raise ValueError # or whichever
if not dictionary.get(key):
return [key]
ret = []
for subkey in dictionary[key]:
ret.extend(func(subkey, dictionary, current.union({key})))
return ret
Print(Counter(func("A"), parts))
#by officerthegeeks
I'm trying to make a function to find the highest score in a directed graph. I do have a start node and can't go throught the same node twice. I've tried to use recursive to get the sum of values until I hit one end node. Then I would call back my function to the start node and try other option ultil I hit another. And so on.
My problem is that when i return to a node with more than one path, the score value for this node is the sum of all paths it can take. And I only want the sum of one specific path.
Here is my code so far:
caminho = list()
def maxscore(start, parentals, score):
global caminho
parentals += start + '|'
if len(graph[start]) > 0:
for i in graph[start]:
if i not in parentals.split('|'):
value = graph[start][i]
if value:
score += value
func = maxscore(i, parentals, score)
else:
continue
if func[0] > score:
score = func[0]
caminho = parentals.split('|')
return score, caminho
else:
return score, start
graph = {
'a': {'b': 2, 'c': 4},
'b': {'d': 5},
'c': {'a': 1, 'e': 3},
'd': {'f': 4},
'e': {'b': 2, 'f': 3, 'g': 2},
'f': {},
'g': {}
}
print(maxscore('a', '', 0))
How could I make it work to return in the end only the best score with the path(caminho) it took.
Sorry if I couldn't make myself clear enough. Feel free to ask any questions.
You probably want to send the score variable by value, but you are sending it by reference, therefore all the possible paths' scores are added to it.
This is my approach:
def maxscore(start, parentals, score):
newParentals = parentals + start + '|'
print newParentals, score
scores = []
if graph[start]:
for nextNode in graph[start]:
if nextNode not in newParentals.split('|'):
scores.append(maxscore(nextNode, newParentals, score + graph[start][nextNode]))
return sorted(scores)[-1]
else:
return score
graph = {
'a': {'b': 2, 'c': 4},
'b': {'d': 5},
'c': {'a': 1, 'e': 3},
'd': {'f': 4},
'e': {'b': 2, 'f': 3, 'g': 2},
'f': {},
'g': {}
}
print(maxscore('a', '', 0))
And this is what gets printed:
a| 0
a|c| 4
a|c|e| 7
a|c|e|b| 9
a|c|e|b|d| 14
a|c|e|b|d|f| 18
a|c|e|g| 9
a|c|e|f| 10
a|b| 2
a|b|d| 7
a|b|d|f| 11
18
You can see how it checks all possible paths, and then picks the highest score :D
Here is an approach:
def maxscore(start, path, score):
best_score = -1
best_i = None
best_path = None
current_path = path + [start]
for i in graph[start]:
if not i in path:
score_i, path_i = maxscore(i, current_path, score + graph[start][i])
if score_i > best_score:
best_score = score_i
best_i = i
best_path = path_i
if best_i is None:
return score, current_path
else:
return best_score, best_path
graph = {
'a': {'b': 2, 'c': 4},
'b': {'d': 5},
'c': {'a': 1, 'e': 3},
'd': {'f': 4},
'e': {'b': 2, 'f': 3, 'g': 2},
'f': {},
'g': {}
}
print(maxscore('a', [], 0))
Output:
(18, ['a', 'c', 'e', 'b', 'd', 'f'])
Notes:
In the code of the question caminho and parentals served the same function. Just having a list is easier to work with than with a string.
In the for-loop, we test each of the edges. If the end-node of the edge is not yet in the path, we calculate the maximum score following that edge. We have to compare each of the possible edges and keep the one with the best score. We can only return from maxscore() after testing all possible edges.
If no free edge was found, we just return the current score and path
If free edges were found, we return the score and path of the best edge
PS: After revising the code, it can be shortened a bit. best_iis not really needed, and the test if best_i is None can be replaced by if best_path is None.
Also, if you need the path in the string form, you can print("|".join(best_path)).
Im having trouble with Dijkstra's algorithm in python. I understand how the Dijkstra's algorithm works, but im not that good in converting it into code. Is there any way to add the nodes of the path and print them out. I keep getting the path. Thank you.
import heapq
import sys
x = raw_input()
y = raw_input()
class Graph:
def __init__(self):
self.vertices = {}
def add_vertex(self, name, edges):
self.vertices[name] = edges
def shortest_path(self, start, finish):
distances = {} # Distance from start to node
previous = {} # Previous node in optimal path from source
nodes = [] # Priority queue of all nodes in Graph
for vertex in self.vertices:
if vertex == start: # Set root node as distance of 0
distances[vertex] = 0
heapq.heappush(nodes, [0, vertex])
else:
distances[vertex] = sys.maxint
heapq.heappush(nodes, [sys.maxint, vertex])
previous[vertex] = None
while nodes:
smallest = heapq.heappop(nodes)[1] # Vertex in nodes with smallest distance in distances
if smallest == finish: # If the closest node is our target we're done so print the path
path = []
while previous[smallest]: # Traverse through nodes til we reach the root which is 0
path.append(smallest)
smallest = previous[smallest]
return path
if distances[smallest] == sys.maxint: # All remaining vertices are inaccessible from source
break
for neighbor in self.vertices[smallest]: # Look at all the nodes that this vertex is attached to
alt = distances[smallest] + self.vertices[smallest][neighbor] # Alternative path distance
if alt < distances[neighbor]: # If there is a new shortest path update our priority queue (relax)
distances[neighbor] = alt
previous[neighbor] = smallest
for n in nodes:
if n[1] == neighbor:
n[0] = alt
break
heapq.heapify(nodes)
return distances
def __str__(self):
return str(self.vertices)
g = Graph()
g.add_vertex('A', {'B': 7, 'C': 8})
g.add_vertex('B', {'A': 7, 'F': 2})
g.add_vertex('C', {'A': 8, 'F': 6, 'G': 4})
g.add_vertex('D', {'F': 8})
g.add_vertex('E', {'H': 1})
g.add_vertex('F', {'B': 2, 'C': 6, 'D': 8, 'G': 9, 'H': 3})
g.add_vertex('G', {'C': 4, 'F': 9})
g.add_vertex('H', {'E': 1, 'F': 3})
print g.shortest_path(x, y)
So I was able to figure out how to use algorithm. Here what I came up with.
import heapq
x = raw_input()
y = raw_input()
def shortestPath(start, end):
queue,seen = [(0, start, [])], set()
while True:
(cost, v, path) = heapq.heappop(queue)
if v not in seen:
path = path + [v]
seen.add(v)
if v == end:
return cost, path
for (next, c) in graph[v].iteritems():
heapq.heappush(queue, (cost + c, next, path))
graph = {
'a': {'w': 14, 'x': 7, 'y': 9},
'b': {'w': 9, 'z': 6},
'w': {'a': 14, 'b': 9, 'y': 2},
'x': {'a': 7, 'y': 10, 'z': 15},
'y': {'a': 9, 'w': 2, 'x': 10, 'z': 11},
'z': {'b': 6, 'x': 15, 'y': 11},
}
cost, path = shortestPath(x, y)
print cost, path
I have the following attempt at a recursive function to create a shift table within a dictionary:
def createShiftTable(alphabet,T,shiftT):
if not T:
for char in alphabet:
shiftT[char] = len(T)+1
return shiftT
else:
return createShiftTable(alphabet,T[1:],shiftT)
shiftT[T[0]] = len(T)
al=['a','b','c','d']
T = "aaabbdddaaba"
print(createShiftTable(al,T,{}))
This is returning {'a': 1, 'c': 1, 'b': 1, 'd': 1}
I'd like it to return {'a': 1, 'c': 13, 'b': 2, 'd': 5}
Non-recursively the following works ok but how do I get the above recursive function working?
def createShiftTableX(alphabet,T):
shiftT={}
for char in al:
shiftT[char] = len(T)+1
for i in range(len(T)):
shiftT[T[i]] = len(T)-i
return shiftT
al=['a','b','c','d']
T = "aaabbdddaaba"
print(createShiftTableX(al,T))
How about this:
def createShiftTableX(alphabet, T):
l = len(T)
for c in alphabet:
i = T.find(c)
if i<0:
yield l
else:
yield i+1
al=['a','b','c','d']
T = "aaabbdddaaba"
print(list(createShiftTableX(al,T)))
You should run this in a debugger or put in some logging or print statements to see what it is actually doing.