How to find indirect relation? [Python] - python

So I'm trying to find indirect relations in a dictionary but I can't seem to find a general code for my program: this is what I have
#find if A is related to E
data = {"A": {"B": 5, "C": 7}, "B": {"E": 8}, "C": {}, "D": {}, "E": {"D": 9}}
if "E" in data["A"]:
result = True
if "E" in data["B"] or "D" in data["C"]:
result = True
else:
result = False
print(result)
#output = True because "E" is in data["A"]
For this one example it works and ofcourse I've could generalize this with x's and y's but if I have a data variable with a complexer dictionary it wouldn't work. Maybe recursive code or a for loop? If somebody could help, it would be very much appreciated.
Thank you in advance

for k,v in data.items():
for l,u in data.items():
if k in u:
print(f"{k} in {u}")
so that the desired function might be :
def has_indirect_rel(dico):
for k,v in dico.items():
for l,u in dico.items():
if k in u: return True
return False

First, the numbers aren't of interest to the problem at hand, so let's reduce the data from dict of dictionaries to dict of sets:
data = {'A': {'B', 'C'}, 'B': {'E'}, 'C': {}, 'D': {}, 'E': {'D'}}
We could search the data recursively:
def has_relation(mapping, a, b):
if b in mapping[a]:
return True
for c in mapping[a]:
if has_relation(mapping, c, b):
return True
return False
print(has_relation(data, 'A', 'D'))
print(has_relation(data, 'A', 'E'))
print(has_relation(data, 'A', 'F'))

Related

In Python, how do I find keys in an array of dictionaries where the values are the same?

I'm using Python 3.8. I have an array of dictionaries, all of which have the same keys ...
list_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
How do I return a list of keys in which all the values are identical? For example, the above would be just
["a"]
since all three dicts have "a" = 1.
Here is my additional answer of #JaonHax.
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz", "c": "cc"}, {"a": 1, "b": "2"}]
def get_same_vals(dicts):
keys = []
for key in dicts[0].keys():
is_same = True
for each_dict in array_of_dicts:
if not key in each_dict or each_dict[key] != dicts[0][key]:
is_same = False
if is_same:
keys.append(key)
return keys
print(get_same_vals(array_of_dicts))
As suggested in other answers, create a master dictionary that groups each key, then check their uniqueness.
# all keys are the same, so get the list
keys = array_of_dicts[0].keys()
# collapse values into a single dictionary
value_dict = {k: set(d[k] for d in array_of_dicts) for k in keys}
# get list of all single-valued keys
print([k for k, v in value_dict.items() if len(v) == 1])
If you know for certain that they all have the same keys, you can iterate through their keys and the list like so:
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
def get_same_vals(dicts):
keys = []
for key in dicts[0].keys():
is_same = True
for each_dict in dicts:
if each_dict[key] != dicts[0][key]:
is_same = False
if is_same:
keys.append(key)
return keys
print(get_same_vals(array_of_dicts))
# Prints ['a']
I apologise for the inefficient code; I didn't spend that long coding this up.
Here's a possible solution, it will work also in case that the dictionaries are differently structured (have different / extra keys):
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
def is_entry_in_all_dicts(key, value):
identical_entries_found = 0
for dict in array_of_dicts:
if key in dict:
if dict[key] == value:
identical_entries_found += 1
if identical_entries_found == len(array_of_dicts):
return True
return False
result = []
for dict in array_of_dicts:
for key, value in dict.items():
if is_entry_in_all_dicts(key, value):
if key not in result:
result.append(key)
print(result)
Output
['a']
If every dictionary has the same keys, you can combine the values into sets and find the sets with one element:
[list(x.keys())[0] for x in [{k:set([e[k] for e in list_of_dicts])} for k in list_of_dicts[0]] if len(list(x.values())[0]) == 1]
Output:
['a']
Append your N dictionaries into a giant dictionary, and check for keys that have N identical values:
giant_dict = collections.defaultdict(list)
for k, v in (e for d in list_of_dicts for e in d):
giant_dict[k].append(v)
for k, v in giant_dict.items():
if len(v) == len(list_of_dicts) and all(e == v[0] for e in v):
print(k)
Here's a more succinct approach
from functools import reduce
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "c": "zz"}, {"a": 1, "d": "2"}]
result = reduce(lambda a, b: a.intersection(b),list(map(lambda x: set(x.keys()),
array_of_dicts)))
Group your values into a set, then figure out which keys have a set with length 1:
>>> from collections import defaultdict
>>> list_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
>>> grouped_values = defaultdict(set)
>>> for d in list_of_dicts:
... for k,v in d.items():
... grouped_values[k].add(v)
...
>>> [k for k,v in grouped_values.items() if len(v) == 1]
['a']

Python: Clean way to update subdicts?

Let's say we have these two dictionaries:
a = {"A": "MyText", "B": {"Sub": "Hello", "NextSub": "Bye"}}
b = {"B": {"NextSub": 55}}
How to merge them together so that I get this result (such that it will work with every type of dictionary)?
ab = {"A": "MyText", "B": {"Sub": "Hello", "NextSub": 55}}
a.update(b) just replaces "B".
I want to merge some dicts because I need to handle with them all. So it's faster if I handle with one merged dict which contains the latest information of all dicts instead of handling with more dicts in a for-loop which would be slower.
Thank you!
For a generic solution, you can use recursion:
l = [[{"A": "MyText", "B": {"Sub": "Hello", "NextSub": "Bye"}},{"B": {"NextSub": 55}}], [{"a": {"a": {"a": 2, "b": "bye"}}}, {"a": {"a": {"a": "Hello"}}}]]
def update(a, b):
if len(a) == len(b):
return {c:d if not isinstance(d, dict) else {**d, **h} if c == e and all(not isinstance(i, dict) for _, i in d.items()) else update(d, h) for [c, d], [e, h] in zip(a.items(), b.items())}
return {c:d if not isinstance(d, dict) else {**d, **b[c]} if all(not isinstance(i, dict) for _, i in d.items()) else update(d, b) for c, d in a.items()}
results = [update(*i) for i in l]
Output:
[{'A': 'MyText', 'B': {'Sub': 'Hello', 'NextSub': 55}}, {'a': {'a': {'a': 'Hello', 'b': 'bye'}}}]

make a dict/json from string with duplicate keys Python

I have a string that could be parsed as a JSON or dict object. My string variable looks like this :
my_string_variable = """{
"a":1,
"b":{
"b1":1,
"b2":2
},
"b": {
"b1":3,
"b2":2,
"b4":8
}
}"""
When I do json.loads(my_string_variable), I have a dict but only the second value of the key "b" is kept, which is normal because a dict can't contain duplicate keys.
What would be the best way to have some sort of defaultdict like this :
result = {
"a": 1,
"b": [{"b1": 1, "b2": 2}, {"b1": 3, "b2": 2, "b4": 8}],
}
I have already looked for similar questions but they all deal with dicts or lists as an input and then create defaultdicts to handle the duplicate keys.
In my case I have a string variable and I would want to know if there is a simple way to achieve this.
something like the following can be done.
import json
def join_duplicate_keys(ordered_pairs):
d = {}
for k, v in ordered_pairs:
if k in d:
if type(d[k]) == list:
d[k].append(v)
else:
newlist = []
newlist.append(d[k])
newlist.append(v)
d[k] = newlist
else:
d[k] = v
return d
raw_post_data = '{"a":1, "b":{"b1":1,"b2":2}, "b": { "b1":3, "b2":2,"b4":8} }'
newdict = json.loads(raw_post_data, object_pairs_hook=join_duplicate_keys)
print (newdict)
Please note that above code depends on value type, if type(d[k]) == list. So if original string itself gives a list then there could be some error handling required to make the code robust.
Accepted answer is perfectly fine. I just wanted to show another approach.
So at first, you dedicate a list for values in order to easily accumulate next values. At the end, you call pop on the lists which have only one item. This means that the list doesn't have duplicate values:
import json
from collections import defaultdict
my_string_variable = '{"a":1, "b":{"b1":1,"b2":2}, "b": { "b1":3, "b2":2,"b4":8} }'
def join_duplicate_keys(ordered_pairs):
d = defaultdict(list)
for k, v in ordered_pairs:
d[k].append(v)
return {k: v.pop() if len(v) == 1 else v for k, v in d.items()}
d = json.loads(my_string_variable, object_pairs_hook=join_duplicate_keys)
print(d)
output:
{'a': 1, 'b': [{'b1': 1, 'b2': 2}, {'b1': 3, 'b2': 2, 'b4': 8}]}

How to use dict.get() with multidimensional dict?

I have a multidimensional dict, and I'd like to be able to retrieve a value by a key:key pair, and return 'NA' if the first key doesn't exist. All of the sub-dicts have the same keys.
d = { 'a': {'j':1,'k':2},
'b': {'j':2,'k':3},
'd': {'j':1,'k':3}
}
I know I can use d.get('c','NA') to get the sub-dict if it exists and return 'NA' otherwise, but I really only need one value from the sub-dict. I'd like to do something like d.get('c['j']','NA') if that existed.
Right now I'm just checking to see if the top-level key exists and then assigning the sub-value to a variable if it exists or 'NA' if not. However, I'm doing this about 500k times and also retrieving/generating other information about each top-level key from elsewhere, and I'm trying to speed this up a little bit.
How about
d.get('a', {'j': 'NA'})['j']
?
If not all subdicts have a j key, then
d.get('a', {}).get('j', 'NA')
To cut down on identical objects created, you can devise something like
class DefaultNASubdict(dict):
class NADict(object):
def __getitem__(self, k):
return 'NA'
NA = NADict()
def __missing__(self, k):
return self.NA
nadict = DefaultNASubdict({
'a': {'j':1,'k':2},
'b': {'j':2,'k':3},
'd': {'j':1,'k':3}
})
print nadict['a']['j'] # 1
print nadict['b']['j'] # 2
print nadict['c']['j'] # NA
Same idea using defaultdict:
import collections
class NADict(object):
def __getitem__(self, k):
return 'NA'
#staticmethod
def instance():
return NADict._instance
NADict._instance = NADict()
nadict = collections.defaultdict(NADict.instance, {
'a': {'j':1,'k':2},
'b': {'j':2,'k':3},
'd': {'j':1,'k':3}
})
Another way to get multidimensional dict example ( use get method twice)
d.get('a', {}).get('j')
Here's a simple and efficient way to do it with ordinary dictionaries, nested an arbitrary number of levels. The example code works in both Python 2 and 3.
from __future__ import print_function
try:
from functools import reduce
except ImportError: # Assume it's built-in (Python 2.x)
pass
def chained_get(dct, *keys):
SENTRY = object()
def getter(level, key):
return 'NA' if level is SENTRY else level.get(key, SENTRY)
return reduce(getter, keys, dct)
d = {'a': {'j': 1, 'k': 2},
'b': {'j': 2, 'k': 3},
'd': {'j': 1, 'k': 3},
}
print(chained_get(d, 'a', 'j')) # 1
print(chained_get(d, 'b', 'k')) # 3
print(chained_get(d, 'k', 'j')) # NA
It could also be done recursively:
# Recursive version.
def chained_get(dct, *keys):
SENTRY = object()
def getter(level, keys):
return (level if keys[0] is SENTRY else
'NA' if level is SENTRY else
getter(level.get(keys[0], SENTRY), keys[1:]))
return getter(dct, keys+(SENTRY,))
Although this way of doing it isn't quite as efficient as the first.
Rather than a hierarchy of nested dict objects, you could use one dictionary whose keys are a tuple representing a path through the hierarchy.
In [34]: d2 = {(x,y):d[x][y] for x in d for y in d[x]}
In [35]: d2
Out[35]:
{('a', 'j'): 1,
('a', 'k'): 2,
('b', 'j'): 2,
('b', 'k'): 3,
('d', 'j'): 1,
('d', 'k'): 3}
In [36]: timeit [d[x][y] for x,y in d2.keys()]
100000 loops, best of 3: 2.37 us per loop
In [37]: timeit [d2[x] for x in d2.keys()]
100000 loops, best of 3: 2.03 us per loop
Accessing this way looks like it's about 15% faster. You can still use the get method with a default value:
In [38]: d2.get(('c','j'),'NA')
Out[38]: 'NA'
For a functional approach very similar to martineau's answer, I've gone with the following:
def chained_get(dictionary: dict, *args, default: Any = None) -> Any:
"""
Get a value nested in a dictionary by its nested path.
"""
value_path = list(args)
dict_chain = dictionary
while value_path:
try:
dict_chain = dict_chain.get(value_path.pop(0))
except AttributeError:
return default
return dict_chain
It's a slightly simpler implementation but is still recursive and optionally allows a default value.
The usage is identical to martineau's answer:
from typing import Any
def chained_get(dictionary: dict, *args, default: Any = None) -> Any:
"""
Get a value nested in a dictionary by its nested path.
"""
value_path = list(args)
dict_chain = dictionary
while value_path:
try:
dict_chain = dict_chain.get(value_path.pop(0))
except AttributeError:
return default
return dict_chain
def main() -> None:
dct = {
"a": {"j": 1, "k": 2},
"b": {"j": 2, "k": 3},
"d": {"j": 1, "k": 3},
}
print(chained_get(dct, "a", "j")) # 1
print(chained_get(dct, "b", "k")) # 3
print(chained_get(dct, "k", "j")) # None
print(chained_get(dct, "k", "j", default="NA")) # NA
if __name__ == "__main__":
main()

Combine two dictionaries of dictionaries (Python)

Is there an easy way to combine two dictionaries of dictionaries in Python? Here's what I need:
dict1 = {'A' : {'B' : 'C'}}
dict2 = {'A' : {'D' : 'E'}}
result = dict_union(dict1, dict2)
# => result = {'A' : {'B' : 'C', 'D' : 'E'}}
I created a brute-force function that does it, but I was looking for a more compact solution:
def dict_union(train, wagon):
for key, val in wagon.iteritems():
if not isinstance(val, dict):
train[key] = val
else:
subdict = train.setdefault(key, {})
dict_union(subdict, val)
Here is a class, RUDict (for Recursive-Update dict) that implements the behaviour you're looking for:
class RUDict(dict):
def __init__(self, *args, **kw):
super(RUDict,self).__init__(*args, **kw)
def update(self, E=None, **F):
if E is not None:
if 'keys' in dir(E) and callable(getattr(E, 'keys')):
for k in E:
if k in self: # existing ...must recurse into both sides
self.r_update(k, E)
else: # doesn't currently exist, just update
self[k] = E[k]
else:
for (k, v) in E:
self.r_update(k, {k:v})
for k in F:
self.r_update(k, {k:F[k]})
def r_update(self, key, other_dict):
if isinstance(self[key], dict) and isinstance(other_dict[key], dict):
od = RUDict(self[key])
nd = other_dict[key]
od.update(nd)
self[key] = od
else:
self[key] = other_dict[key]
def test():
dict1 = {'A' : {'B' : 'C'}}
dict2 = {'A' : {'D' : 'E'}}
dx = RUDict(dict1)
dx.update(dict2)
print(dx)
if __name__ == '__main__':
test()
>>> import RUDict
>>> RUDict.test()
{'A': {'B': 'C', 'D': 'E'}}
>>>
This solution is pretty compact. It's ugly, but you're asking for some rather complicated behavior:
dict_union = lambda d1,d2: dict((x,(dict_union(d1.get(x,{}),d2[x]) if
isinstance(d2.get(x),dict) else d2.get(x,d1.get(x)))) for x in
set(d1.keys()+d2.keys()))
My solution is designed to combine any number of dictionaries as you had and could probably be cut down to look neater by limiting it to combining only two dictionaries but the logic behind it should be fairly easy to use in your program.
def dictCompressor(*args):
output = {x:{} for mydict in args for x,_ in mydict.items()}
for mydict in args:
for x,y in mydict.items():
output[x].update(y)
return output
You could subclass dict and wrap the original dict.update() method with a version which would call update() on the subdicts rather than directly overwriting subdicts. That may end up taking at least as much effort as your existing solution, though.
Has to be recursive, since dictionaries may nest. Here's my first take on it, you probably want to define your behavior when dictionaries nest at different depths.
def join(A, B):
if not isinstance(A, dict) or not isinstance(B, dict):
return A or B
return dict([(a, join(A.get(a), B.get(a))) for a in set(A.keys()) | set(B.keys())])
def main():
A = {'A': {'B': 'C'}, 'D': {'X': 'Y'}}
B = {'A': {'D': 'E'}}
print join(A, B)
As for me there is not enaugh information but anyway please find my sample code below:
dict1 = {'A' : {'B' : 'C'}}
dict2 = {'A' : {'D' : 'E'}, 'B':{'C':'D'}}
output = {}
for key in (set(dict1) | set(dict2):
output[key] = {}
(key in dict1 and output[key].update(dict1.get(key)))
(key in dict2 and output[key].update(dict2.get(key)))

Categories

Resources