comparing inner dictionaries in Python - python

I am trying to create a Python function that receives a dictionary whose values are inner dictionaries. If the keys of the inner dictionaries are the same, it should return 1, if not it should return 0.
This is the code I tried:
def f(dct: dict) -> int:
for i in range(len(dct)):
for j in range(len(dct)):
dct1 = list(dct.values())
if dct1[i].keys() == dct1[j].keys():
return 1
else:
return 0
it actually worked when the input dictionary have only two inner dictionaries but didn't work for three.
For example:
f(
{
"A": {1: "a", 2: "b"},
"B": {2: "c", 3: "d"},
}
)
returned 0 (which is the result I wanted)
but
f(
{
"A": {1: "a", 2: "b"},
"B": {2: "c", 3: "d"},
"C": {1: "c", 2: "d"},
}
)
returned 1, which is not the result I wanted.
How do I fix it, please?

So you want to ensure all of the dictionaries that are the values of dct have the same keys (ignoring the values)?
def all_key_sets_equal(dct: dict) -> bool:
key_sets = [set(nd) for nd in dct.values()]
return all(key_set == key_sets[0] for key_set in key_sets)

Related

Switching keys of a dictionary without switching the values

Let's say I have a dictionary like below
myDict = {"a": 1, "b": 2, "c": 3, "d": 4}
and I'm trying to get this result
myDict = {"b": 1, "a": 2, "c": 3, "d": 4}
I tried running using
dictionary[new_key] = dictionary.pop(old_key)
But thats just deleting and appending a new key and value to the dictionary. It would result in:
myDict = {"b": 2, "c": 3, "d": 4, "a": 2}
Thanks in advance for the answer
So I understand you aim to preserve the sequence.
Make first a new dictionary that maps the old keys to the new keys:
mapping = {"a": "b", "b": "a"}
Now you can generate the new structure
my_dict = {mapping.get(key, key): value for key, value in my_dict.items()}
The get method here tries to map the key, but uses the key itself, if the key is not in the mapping.

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']

Iterate over a list inside a nested dictionary

Lets say I have a dictionary like this:
myDict = {
1: {
"a": "something",
"b": [0, 1, 2],
"c": ["a", "b", "c"]
},
2: {
"a": "somethingElse",
"b": [3, 4, 5],
"c": ["d", "e", "f"]
},
3: {
"a": "another",
"b": [6, 7, 8],
"c": ["g", "h", "i"]
}
}
And this is my code:
for id, obj in myDict.items():
for key, val in obj.items():
if key is "b":
for item in val:
# apply some function to item
Is there a better way to iterate over a list inside a nested dict? Or is there a pythonic way to do this?
You absolutely do not need to iterate the list to print it (unless this is a functional requirement for the code you are writing).
Very simply, you could do this:
for id, obj in myDict.items():
if "b" in obj:
print obj["b"]
To map the list object, represented by obj['b'] to another function, you can use the map function:
map(foo, obj["b"])
If you're dictionary is always two levels deep, I don't see anything wrong with your approach. In your implementation, I would use key == "b" rather than key is "b". Using is will test for identity (e.g. id(a) == id(b)), while == will test for equality (e.g. a.__eq__(b)). This functions the same way when I test it in IDLE, but it's not a good habit to get into. There's more info on it here: How is the 'is' keyword implemented in Python?
If you want to deal with varying level dictionaries, you could use something like:
def test_dict_for_key(dictionary, key, function):
for test_key, value in dictionary.items():
if key == test_key:
dictionary[key] = type(value)(map(function, value))
if isinstance(value, dict):
test_dict_for_key(value, key, function)
An example usage might be something like:
myDict = {
1: {
"a": "something",
"b": [0, 1, 2],
"c": ["a", "b", "c"]
},
2: {
"a": "somethingElse",
"b": [3, 4, 5],
"c": ["d", "e", "f"]
},
3: {
"a": "another",
"b": [6, 7, 8],
"c": ["g", "h", "i"]
}
}
# adds 1 to every entry in each b
test_dict_for_key(myDict, "b", lambda x: x + 1)
# prints [1, 2, 3]
print(myDict[1]["b"])
I'm a fan of generator expressions.
inner_lists = (inner_dict['b'] for inner_dict in myDict.values())
# if 'b' is not guaranteed to exist,
# replace inner_dict['b'] with inner_dict.get('b', [])
items = (item for ls in inner_lists for item in ls)
Now you can either use a foo loop
for item in items:
# apply function
or map
transformed_items = map(func, items)
A couple fixes could be made.
Don't use is when comparing two strings (if key is "b":)
Simply say print(item) instead of using .format(), since you only have one variable that you're printing, with no additional string formatting
Revised code:
for id, obj in myDict.items():
for key, val in obj.items():
if key == "b":
for item in val:
print(item)
If you are sure that you will have a b key in every case, you can simply do:
for id, obj in myDict.items():
for item in obj["b"]:
print item

Merge values of a dictionary by key based on custom function

Assume you have two dictionaries and you want to merge the two dictionaries by applying a function to the values that have matching keys. here I use the + operator as binary function.
x = { 1: "a", 2: "b", 3: "c" }
y = { 1: "A", 2: "B", 3: "C" }
result = { t[0][0]: t[0][1] + t[1][1] for t in zip(sorted(x.items()), sorted(y.items())) }
print result # gives { 1: "aA", 2: "bB", 3: "cC" }
I would prefer a self contained expression instead of statements, but this is unreadable.
so far I'm doing:
def dzip(f, a, b):
least_keys = set.intersection(set(a.keys()), set(b.keys()))
copy_dict = dict()
for i in least_keys.keys():
copy_dict[i] = f(a[i], b[i])
return copy_dict
print dzip(lambda a,b: a+b,x,y)
Is there a more readable solution to this than the expression I gave?
In the first case, you can directly use a dict comprehension:
>>> x = { 1: "a", 2: "b", 3: "c" }
>>> y = { 1: "A", 2: "B", 3: "C" }
>>> {key: x.get(key, "") + y.get(key, "") for key in set.intersection(set(x.keys()), set(y.keys()))}
{1: 'aA', 2: 'bB', 3: 'cC'}
So that in your second piece of code, you can simplify it to a simple one liner:
def dzip(f, a, b):
return {key: f(a.get(key, ""), b.get(key, "")) for key in set.inersection(set(a.keys()) + set(b.keys()))}
You can even define dzip as a lambda:
dzip = lambda f, a, b: {key: f(a.get(key, ""), b.get(key, ""))
for key in set.intersection(set(a.keys()), set(b.keys()))}
In a single run, this becomes:
>>> dzip = lambda f, a, b: {key: f(a.get(key, ""), b.get(key, ""))
... for key in set.intersection(set(a.keys()), set(b.keys()))}
>>>
>>> print dzip(lambda a,b: a+b,x,y)
{1: 'aA', 2: 'bB', 3: 'cC'}
Note that this will work even if x and y have different sets of keys (just something that can break in your first version of the code).
You can use Counter for this type of dict merging
from collections import Counter
>>>Counter(x)+Counter(y)
Counter({3: 'cC', 2: 'bB', 1: 'aA'})

How to dynamically move inside a nested dict in Python

I have a dict with a dynamical number of nested dicts inside of it, something like:
my_dict = {"a": {"b": {"c: {...}}}}
I need to dynamically move inside this dict, for instance I'd like to do the following:
levels = ["a", "b", "c"]
my_dict[levels[0]][levels[1]][levels[2]] = "something"
where the number of items inside "levels" may vary.
I can partially achieve the same result for a limited number of items inside "levels" by writing something like this:
if len(levels) == 1:
my_dict[levels[0]] = "something"
elif len(levels) == 2:
my_dict[levels[0]][levels[1]] = "something"
elif len(levels) == 3:
my_dict[levels[0]][levels[1]][levels[2]] = "something"
(...)
but I'm looking for a more general and elegant solution.
Is there a way to do this?
There isn't a lot of code here to go on, but for what you have given, you can define
def get(d, keys):
for key in keys:
d = d[key]
return d
def set(d, keys, value):
d = get(d, keys[:-1])
d[keys[-1]] = value
And then use it like this
my_dict = {"a":{"b":{"c":{}}}}
set(my_dict, ["a", "b", "c"], "something")
print get(my_dict, ["a", "b", "c"])
A functional alternative for get:
def get(d, keys):
return reduce(lambda d, key: d[key], keys, d)

Categories

Resources