Check if dict values exist as keys in another dict - python

I have two dictionaries:
concave = {6: [2, 3, 4, 5], 2: [6], 3: [6], 4: [6], 5: [6]}
convex = {1: [2, 3, 4, 5], 2: [1, 3, 5], 3: [1, 2, 4], 4: [1, 3, 5], 5: [1, 2, 4], 6: [7, 8, 9, 10], 7: [6, 8, 10, 11], 8: [6, 7, 9, 11], 9: [6, 8, 10, 11], 10: [6, 7, 9, 11], 11: [7, 8, 9, 10]}
And I have returned the keys which have max length values in the convex dict:
max_lens = [1, 6, 7, 8, 9, 10, 11]
For each number in max_lens, I want to check that it does not exist as a key in concave and its values in convex exist as keys in concave.
So in this example, '1' would satisfy this condition as it is not included in concave as a key, but its values in convex are (i.e. 2, 3 4 and 5).
I have tried to figure out how to go about this using for loops/if statements:
for i in enumerate(max_lens):
if i not in concave:
for k,v in convex.items():
for j in v:
That is about as far as I got before getting totally confused. There must be an easier way to do this other than using multiple for loops and if statements?
I'm a bit of a python noob so sorry if this comes across as confusing!

I think I understood (for the record I prefer the explicit concave.keys())
result_dict = {}
for convex_key in max_lens:
result_dict[convex_key] = convex_key not in concave.keys() \
and all(convex_val in concave.keys()
for convex_val in convex[convex_key])
Edit (see comments)
for convex_key in max_lens:
if convex_key not in concave.keys() and \
all(convex_val in concave.keys() for convex_val in convex[convex_key]):
top_face = convex_key
break

Spelling this problem out into steps always helps:
Loop over each of the lengths l in max_lens
Check if l doesn't exist in concave but exists in convex. A conjunction of these two conditions is needed here. If either fails, don't continue.
If the above two conditions are accepted, check if all the values from convex[l] exist in concave.
If the code reaches here with no issues, all the conditions are met.
Demo:
concave = {6: [2, 3, 4, 5], 2: [6], 3: [6], 4: [6], 5: [6]}
convex = {1: [2, 3, 4, 5], 2: [1, 3, 5], 3: [1, 2, 4], 4: [1, 3, 5], 5: [1, 2, 4], 6: [7, 8, 9, 10], 7: [6, 8, 10, 11], 8: [6, 7, 9, 11], 9: [6, 8, 10, 11], 10: [6, 7, 9, 11], 11: [7, 8, 9, 10]}
max_lens = [1, 6, 7, 8, 9, 10, 11]
for l in max_lens:
if l not in concave and l in convex and all(v in concave for v in convex[l]):
print(l)
Output:
1

You can do it with comprehension:
[i for i in max_lens if i not in concave and convex[i] in concave.values()]

Using a simple forloop.
concave = {6: [2, 3, 4, 5], 2: [6], 3: [6], 4: [6], 5: [6]}
convex = {1: [2, 3, 4, 5], 2: [1, 3, 5], 3: [1, 2, 4], 4: [1, 3, 5], 5: [1, 2, 4], 6: [7, 8, 9, 10], 7: [6, 8, 10, 11], 8: [6, 7, 9, 11], 9: [6, 8, 10, 11], 10: [6, 7, 9, 11], 11: [7, 8, 9, 10]}
max_lens = [1, 6, 7, 8, 9, 10, 11]
for i in max_lens:
if (i not in concave): #Check if not in key.
if convex[i] in concave.values(): #Check Value.
print i
Output:
1

If you don't understand a problem easily, it's often a good way, to divide it into several smaller problems:
write a function that checks if a value is not key of a dict:
def is_no_key_in(v, _dict):
return key not in _dict
Since that is too simple return a list of keys that are not in dict:
def no_key_values(_list, _dict):
return [ v for v in _list if is_no_key_in(v, _dict) ]
Now that you only have values that fit your first condition, you can concentrate on your second condition. Since you want that every value of a list is in a list of keys, you can start making a union-like function:
def union(a_lst, b_lst):
return [ a for a in a_lst if a in b_lst]
To make it more into something serving your needs, you could change it to a function that checks for any diffs:
def is_subset(a_lst, b_lst):
return len([a for a in a_lst if a not in b_lst]) == 0
Now you piece the functions together:
def satisfies_conditions(max_lens):
for lens in no_key_values(max_lens, concave):
if is_subset(convex[lens], concave.keys())
yield lens
result = [ lens for lens in satisfies_conditions(max_lens) ]
result now contains all lenses that satisfy your conditions and if you want to change your conditions, you can easily do so. If your code works, you can go on and refactor it. For example you might not need is_no_key_in as it is a very simple function. Then go on and inline it into no_key_values:
def no_key_values(_list, _dict):
return [ v for v in _list if v not in _dict ]
If you write some tests before refactoring (or even writing the code) you can ensure, that your refactoring won't introduce bugs. Then simplify the code step by step. Maybe you will end up with a solution as simple as proposed in other answers here.
(I hope this will also help you with future problems like that :-))

Related

Split a list into unequal chunks specified by a dictionary

So here is my problem I have a list of size eleven along with a dictionary which tells us how the list should be split. So here the first index should contain the sublist [14, 10, 2, 4], the second index [12, 8, 8, 5] and so on.
l = [14, 10, 2, 4, 12, 8, 8, 5, 9, 2, 7]
dico = dict()
dico[0] = 4
dico[1] = 4
dico[2] = 3
dico
>>> {0: 4, 1: 4, 2: 3}
Eventually the expected behavior is the following
{0:[14, 10, 2, 4], 1:[12, 8, 8, 5], 2:[9, 2, 7]}
Note that retaining the order of the initial list is important.
Given your sample data and a reasonably young Python version (that maintains insertion order for iteration of dicts), you can simply do:
i = iter(l)
{k: [next(i) for _ in range(v)] for k, v in dico.items()}
# {0: [14, 10, 2, 4], 1: [12, 8, 8, 5], 2: [9, 2, 7]}
new_d = {}
for key in dico:
new_d[key] = l[:dico[key]]
l[:dico[key]] = []
print(new_d)
{0: [14, 10, 2, 4], 1: [12, 8, 8, 5], 2: [9, 2, 7]}
I will offer this option:
l = [14, 10, 2, 4, 12, 8, 8, 5, 9, 2, 7]
l2 = l.copy()
dico = {0: 4, 1: 4, 2: 3}
for key, val in dico.items():
for j in range(dico[key]):
if j == 0:
dico[key] = []
dico[key].append(l2.pop(0))
print(dico)
print(l)
Output:
{0: [14, 10, 2, 4], 1: [12, 8, 8, 5], 2: [9, 2, 7]}
[14, 10, 2, 4, 12, 8, 8, 5, 9, 2, 7]

How do I stop iterating based on the number of elements on my list?

I have a project wherein I have to get the follow-up elements of elements in on_time.
For example:
j_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
s_follow_int = [[2, 3], 4, [5, 6], [6, 8], [7, 8], 9, 8, 10, 10, 11]
on_time = [4, 5]
The code I have looks like this:
# element in on_time except 1, get follow_finish_act
follow_finish_act = []
for a, d in zip(j_set, s_follow_int):
if a in on_time and a != 1:
if len(on_time) > 1:
follow_finish_act.append(d)
else:
follow_finish_act = d
Output I am getting:
follow_finish_act = [[6, 8], [7, 8]]
Expected Output:
follow_finish_act = [6, 7, 8]
I am having trouble when length of on_time is more than 1. I think the problem is flattening the irregular lists (can be nested and integer) without duplicates. Since, I cannot get my expected output.
Any help/suggestions would be appreciated! Thank you!
Edit: Code I used for trying to flatten output of follow_finish_act
def flatten(lss):
for item in lss:
try:
yield from flatten(item)
except TypeError:
yield item
You could avoid duplicates by using set instead of list
j_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
s_follow_int = [[2, 3], 4, [5, 6], [6, 8], [7, 8], 9, 8, 10, 10, 11]
on_time = [4, 5]
follow_finish_act = set()
for a, d in zip(j_set, s_follow_int):
if a in on_time and a != 1:
if len(on_time) > 1:
follow_finish_act.update(d)
else:
follow_finish_act.update(d)
print(follow_finish_act)
# prints {6,7,8}
print(list(follow_finish_act))
# prints[8,7,6]
Its difficult to tell what you really want, but looking at the code a lot of it seems superfluous. Try this instead:
j_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
s_follow_int = [[2, 3], 4, [5, 6], 8, 7, 9, 8, 10, 10, 11]
on_time = [6, 5]
follow_finish_act = []
for a, d in zip(j_set, s_follow_int):
if a in on_time:
follow_finish_act.append(d)
print(follow_finish_act)
Output:
[7, 9]
If you then get output like: [9], you could do this afterwards:
if len(follow_finish_act) == 1:
follow_finish_act = follow_finish_act[0]

Expand a python dictionary to express shared list values

Let's say I have the following simple dictionary in Python3.x:
example = {1:[4, 5, 6], 2:[7, 8, 9]}
I would like a way to expand the dictionary as follows:
expanded_example = {1:[4, 5, 6], 2:[7, 8, 9], 4:[5, 6], 5:[4, 6], 6:[4, 5], 7:[8, 9], 8:[7, 9], 9:[7, 8]}
This becomes quite complicated by values shared by multiple keys. As an example,
example2 = {1:[4, 5, 6], 2:[4, 7, 8, 9]}
Here 4 is a value in the lists associated with 1 and 2.
There are two approaches if there are "repeat" value elements:
(1) Only keep values immediately associated with a certain key:
{1:[4, 5, 6], 2:[4, 7, 8, 9], 4:[5, 6], 5:[4, 6], 6:[4, 5], 7:[8, 9], 8:[7, 9], 9:[7, 8]}
(2) Keep all associated values (as '4' is shared between keys '1' and '2'):
{1:[4, 5, 6], 2:[4, 7, 8, 9], 4:[5, 6, 7, 8, 9], 5:[4, 6], 6:[4, 5], 7:[4, 8, 9], 8:[4, 7, 9], 9:[4, 7, 8]}
EDITED:
My thought for this task was to use collections.defaultdict:
from collections import defaultdict
dict1 = {1:[4, 5, 6], 2:[4, 7, 8, 9]}
d_dict = defaultdict(list)
for k,l in dict1.items():
for v in l:
d_dict[v].append(l)
print(d_dict)
## defaultdict(<class 'list'>, {4: [[4, 5, 6], [4, 7, 8, 9]], 5: [[4, 5, 6]], 6: [[4, 5, 6]], 7: [[4, 7, 8, 9]], 8: [[4, 7, 8, 9]], 9: [[4, 7, 8, 9]]})
This gets me some of the way, but there are repeat elements in lists of lists...
strategy 2
example2 = {1:[4, 5, 6], 2:[4, 7, 8, 9]}
output = {**example2}
for val in example2.values():
for idx,v in enumerate(val):
if v not in output:
output[v] = val[0:idx]+val[idx+1:]
else:
output[v].extend(val[0:idx]+val[idx+1:])
print(output)
#{1: [4, 5, 6], 2: [4, 7, 8, 9], 4: [5, 6, 7, 8, 9], 5: [4, 6], 6: [4, 5], 7: [4, 8, 9], 8: [4, 7, 9], 9: [4, 7, 8]}
strategy 1
import copy
example2 = {1:[4, 5, 6], 2:[4, 7, 8, 9]}
output = copy.deepcopy(example2)
for val in example2.values():
for num in val:
if num in output:
val.remove(num)
for idx,v in enumerate(val):
output[v] = val[0:idx]+val[idx+1:]
print(output)
#{1: [4, 5, 6], 2: [4, 7, 8, 9], 4: [5, 6], 5: [4, 6], 6: [4, 5], 7: [8, 9], 8: [7, 9], 9: [7, 8]}
Note: This answer only deals with Approach #1.
You can work with copies of your data, as you should not add/remove dictionary items while iterating a view:
d = {1:[4, 5, 6], 2:[7, 8, 9]}
for k, v in list(d.items()):
for w in v:
L = v.copy()
d[L.pop(L.index(w))] = L
print(d)
{1: [4, 5, 6], 2: [7, 8, 9], 4: [5, 6], 5: [4, 6],
6: [4, 5], 7: [8, 9], 8: [7, 9], 9: [7, 8]}

Use max() function to get keys of dict items with max length

I have two dictionaries like so:
concave relations : {6: [2, 3, 4, 5], 2: [6], 3: [6], 4: [6], 5: [6]}
convex relations : {1: [2, 3, 4, 5], 2: [1, 3, 5], 3: [1, 2, 4], 4: [1, 3, 5], 5: [1, 2, 4], 6: [7, 8, 9, 10], 7: [6, 8, 10, 11], 8: [6, 7, 9, 11], 9: [6, 8, 10, 11], 10: [6, 7, 9, 11], 11: [7, 8, 9, 10]}
Previously I could find the key which corresponds to the item of max length using this code:
bottom_face = max(concave, key=lambda x:len(concave[x]))
Since the concave dict doesn't contain any items of the same length
Since this is not the case in the convex dict, and I want to return all of the keys which have max length items, I tried using the following:
possible_top_faces = [i for i, x in enumerate(convex) if x == max(convex, key=lambda x:len(convex[x]))]
But it is just returning:
[0]
Instead of the keys 1, 6, 7, 8, 9, 10, 11.
Can anyone help me out?
You can get the largest length of any key in convex, and use that as your standard for filtering out other keys in convex:
convex = {
1: [2, 3, 4, 5],
2: [1, 3, 5],
3: [1, 2, 4],
4: [1, 3, 5],
5: [1, 2, 4],
6: [7, 8, 9, 10],
7: [6, 8, 10, 11],
8: [6, 7, 9, 11],
9: [6, 8, 10, 11],
10: [6, 7, 9, 11],
11: [7, 8, 9, 10]
}
longest_len = max(map(len, convex.values()))
max_lens = [k for k, v in convex.items() if len(v) == longest_len]
print(max_lens) # [1, 6, 7, 8, 9, 10, 11]

Combining Lists Until Desired List Is Made

If I have the following Dictionary of Lists:
{1: [2], 2: [1, 3, 5], 3: [2, 4], 4: [3, 7], 5: [2, 6], 6: [5], 7: [8, 4], 8: [7]}
I want to know the keys I'll need a full set of:
[1,2,3,4,5,6,7,8]
Using the smallest number of keys. For this particular problem for example it would be:
keys needed: [2,5,7]
While the order doesn't matter I want to use as few keys as possible. Is there an easy way to do this and to test if my answer is the shortest possible answer?
Here comes the bruteforce solution, using itertools.combinations:
from itertools import combinations
data = {1: [2], 2: [1, 3, 5], 3: [2, 4], 4: [3, 7], 5: [2, 6], 6: [5], 7: [8, 4], 8: [7]}
desired_set = set([1, 2, 3, 4, 5, 6, 7, 8])
n = len(data)
all_values = set(x for values in data.values() for x in values)
if all_values.issuperset(desired_set):
print("There must be a solution...")
n = len(data)
for i in range(0, n + 1):
for combination in combinations(data, i):
values = set(x for key in combination for x in data[key])
if values.issuperset(desired_set):
print("FOUND!")
print("Keys needed : %r" % list(combination))
print("Values : %r" % list(values))
quit()
else:
print "Not possible. Not even trying"
It outputs:
FOUND!
Keys needed : [2, 4, 5, 7]
Values : [1, 2, 3, 4, 5, 6, 7, 8]
Worst case complexity is O(2**n) though, so don't use it on big datasets!
Note that you need 4 as key to get 7 as value.
If you mean values instead of keys, here you go.
d = {1: [2], 2: [1, 3, 5], 3: [2, 4], 4: [3, 7], 5: [2, 6], 6: [5], 7: [8, 4], 8: [7]}
#get the values from all elements and use a set to remove duplicate and then use sum to flatten the list.
list(set(sum(d.values(),[])))
Out[806]: [1, 2, 3, 4, 5, 6, 7, 8]
If you just want the keys in order, you can call sorted() directly on the dictionary.
some_dict = {1: [2], 2: [1, 3, 5], 3: [2, 4], 4: [3, 7], 5: [2, 6], 6: [5], 7: [8, 4], 8: [7]}
keys = sorted(some_dict)
print(keys)
[1, 2, 3, 4, 5, 6, 7, 8]
If, on the other hand, you want all the unique VALUES in the dictionary in sorted order, you should sort a set.
values = sorted({x for key in some_dict for x in some_dict[key]})
print(values)
[1, 2, 3, 4, 5, 6, 7, 8]
I don't know why you would want to use the shortest possible number of keys, but you can do so as follows.
wanted = set(range(1, 9))
seen = set()
for key in some_dict:
seen |= set(some_dict[key])
if seen == wanted:
break

Categories

Resources