Function to check if N number of keys exist in a dictionary - python

I am looking to create a function that will check if multiple keys are present in a dictionary and return a value that indicates which keys were present. I know I could write something like:
def param_checker(parameters):
if 'foo' in parameters:
if 'bar' in parameters:
if 'qux' in parameters:
return 0
else:
return 1
elif 'baz' in parameters:
if 'qux' in parameters:
return 2
else:
return 3
else:
return 4
elif 'quxx' in parameters:
if 'bar' in parameters:
return 5
elif 'qux' in parameters:
if 'baz' in parameters:
return 6
else:
return 7
else:
return 8
else:
return 9
However, the above example is both messy and clunky. The complexity also scales with the number of parameters you need to check to return a value. For example, I needed 3 layers of if conditions to check the keys foo, bar, and qux. I know I could chain the if conditions inline, but it is still messy and clunky.
This is somewhat similar to Python check if list of keys exist in dictionary. The big difference here is that instead of checking if they all exist or not, I am looking to return a value indicating what combination of keys exist.
Note: I did not write out all possible combinations in the provided sample code, as that would be tedious when I am just trying to illustrate the problem.
Below is a example of invoking the function to check which keys are present.
d = dict()
d['foo'] = 'blah'
d['bar'] = 'blah'
val = param_checker(d)
# val represents a unique value indicating which keys are present in dictionary d
I will know ahead of time what keys could be in the dictionary, so I can have some sort of list of possible keys, like:
keys = ["foo", "bar", "baz", "qux", "quxx"]
I am hoping to build a function that can check N keys and return a unique value indicating which ones where present. How can I efficiently do this?

keyseq = ('foo', 'bar', 'quux')
keymap = {key: 1 << e for (e, key) in enumerate(keyseq)}
return sum(keymap[key] for key in keymap if key in D)

Something like this (assuming Python 3)?
from itertools import compress
queries = ["a", "b", "c", "d"]
in_dict = [q in d for q in queries]
existing_keys = compress(queries, in_dict)
You can of course wrap this in a function where queries become the input parameter and d is the input dictionary.

Make a set of your desired keys.
Make a set of the dict's keys.
Take the intersection. Count.
master = {
"foo": 1,
"bar": 5,
"qux": 18,
"lemon": -6,
"quxx": "How boring are examples?",
"dog": False
}
tag = {
"foo", "bar", "baz", "qux", "quxx"
}
master_set = set(master.keys())
found = master_set.intersection(tag)
print (len(found), found)
Output:
4 {'quxx', 'foo', 'qux', 'bar'}

You can try this:
keys = ["foo", "bar", "baz", "qux", "quxx"]
counter = {i:d.keys().count(i) for i in keys}

Related

Sort dictionary by multiple values, where amount of values can vary

To come straight to the point:
def sortBy(dict, byWhat):
# byWhat is a list of 1 - 10 strings, which can include any of allAttributes
# allAttributes = ['name', 'kingdom', 'diff', 'tier', 'type', 'founder', 'prover', 'server', 'extra', 'link']
# the lower the index, the higher the sorting priority -> byWhat = ['name', 'diff'] then sort by name first and then if names are the same by diff
# dict has the name as key and then allAttributes above in order as a list for the key's value, so for example:
# dict[Jump] = [Jump, Mushroom Kingdom, 10/10, Triple Jump, Dude, Dude2, Main Server, Cool jump description, https://twitter.com]
# All dictionary entries have those 10 attributes, no exceptions
return dict # but sorted
I tried for a while but where my knowledge ends is sorting by a varying size of specifiers with lambda. I can also not just call the function multiple times, because then it won't sort by two attributes, but by the last attribute the function got called with.
You can convert dict to list of (key, value) tuples and use list.sort(key) method and utilizing functools.cmp_to_key define key as compare function and then "inject" byWhat parameter into compare function using functools.partial
from functools import cmp_to_key, partial
allAttributes = ["name", "age"]
def sortBy(d, byWhat):
def cmp(a, b, byWhat):
for k in byWhat:
index = allAttributes.index(k)
if a[1][index] != b[1][index]:
return -1 if a[1][index] < b[1][index] else 1
return 0
items = list(d.items())
items.sort(key=cmp_to_key(partial(cmp, byWhat=byWhat)))
return dict(items)
if __name__ == "__main__":
d = {
"foo": ["John", 12],
"bar": ["Adam", 12],
"baz": ["John", 10],
}
print(sortBy(d, ["name", "age"]))
You can convert dict to list of (key, value) tuples, map byWhat into indexes and use list.sort(key) where key is lambda returning list of attributes to sort by.
allAttributes = ["name", "age"]
def sortBy(d, byWhat):
indexes = [allAttributes.index(k) for k in byWhat]
items = list(d.items())
items.sort(key=lambda item: [item[1][index] for index in indexes])
return dict(items)
if __name__ == "__main__":
d = {
"foo": ["John", 12],
"bar": ["Adam", 12],
"baz": ["John", 10],
}
print(sortBy(d, ["name", "age"]))

Iterate adding values to an existing dictionary by using a variety of keys stored as a list

I had trouble coming up with an appropriate title for this, so apologies there.
I have an existing dictionary di_end which already has an order to its keys. I also have some objects which have a property containing the keys for where in di_end the user-entered value will go
Note: the methods setProperty() and property() are from the pyqt library where setProperty() creates a custom property for an object where the first argument is the name of the property and the second argument is the value for that named property and property() just returns the values for whatever name is passed into it as an argument.
Something like this:
a.setProperty('keys', [key1, key2, key3])
b.setProperty('keys', [key4, key5, key6, key7, key8])
c.setProperty('keys', [key9])
objects_list = [a, b, c]
I want to be able to use the keys stored in the object properties to load the value that the user enters into a field to a dictionary
I'd like to iterate the process such that these parts
di_end[a.property(['keys'])[0]][a.property(['keys'])[1]][a.property(['keys'])[2]] = a.value
di_end[b.property(['keys'])[0]][b.property(['keys'])[1]][b.property(['keys'])[2]]\
[b.property(['keys'])[3]][b.property(['keys'])[4]] = b.value
di_end[c.property(['keys'])[0]] = c.value
or
a_li, b_li, c_li = a.property(['keys']), b.property(['keys']), c.property(['keys'])
di_end[a_li[0]][a_li[1]][a_li[2]] = a.value
di_end[b_li[0]][b_li[1]][b_li[2]][b_li[3]][b_li[4]] = b.value
di_end[c_li[0]] = c.value
do not need to be manually typed out and could be performed procedurally. I think I could do this if it was the same amount of keys but I'm not sure how to do it with differing amounts of keys. If they were all the same amounts I'd just do this
a.setProperty('keys', [key1, key2, key3, key4])
b.setProperty('keys', [key5, key6, key7, key8])
c.setProperty('keys', [key9, key10, key11, key12])
objects_list = [a, b, c]
a_li, b_li, c_li = a.property(['keys']), b.property(['keys']), c.property(['keys']) # assuming all are 4 entires each
for count, item in enumerate([a_li, b_li, c_li]):
di_end[item[0]][item[1]][item[2]][item[3]] = objects_list[count].value
but since there are different amounts of keys for each entry, I'm not sure how to accomplish this.
Edit: Added a note about setProperty() and property()
It's pretty hard to tell what your code is doing here, mostly due to the setProperty and property objects, which seem awkward and not-pythonic.
That being said, this looks like yet another use case for the excellent glom library (pip install glom).
from glom import assign, Path
assign(data, Path(*keys), value)
Note that in your case
data = di_end
keys = a.property['keys']
value = a.value
and an example:
>>> data = {'a': {'b': {'c': 5}}}
>>> keys = ['a', 'b', 'c']
>>> value = 100
>>> assign(data, Path(*keys), value)
{'a': {'b': {'c': 100}}}
>>> data
{'a': {'b': {'c': 100}}}
Note that this will raise a KeyError if the path doesn't already exist. For the generate-dict-on-demand (like a infinite nested defaultdict) style, you need to use Assign instead:
glom(data, Assign(Path(*keys), value))
See https://glom.readthedocs.io/en/latest/mutation.html for more details.
It seems you can try use recursion here. A simple example below.
put function creates nested dict and put value. fetch function can extract value from nested dict.
def fetch(data, keys):
if len(keys) == 1:
return data[keys[0]]
return fetch(data[keys.pop()], keys)
def put(data, keys, value):
if len(keys) == 1:
data[keys[0]] = value
return
key = keys.pop()
data[key] = {}
put(data[key], keys, value)
di_empty = {}
keys_values = [
(("a", "b", "c"), "abc"),
(("b", "c", "d", "e"), "bcde")
]
for selected in keys_values:
put(di_empty, list(reversed(selected[0])), selected[1])
di_end = dict()
di_end["a"] = {"b": {"c": "abc"}}
di_end["b"] = {"c":{"d": {"e": "bcde"}}}
keys = [("a", "b", "c"), ("b", "c", "d", "e")]
for selected in keys:
s1 = fetch(di_end, list(reversed(selected)))
s2 = fetch(di_empty, list(reversed(selected)))
assert s1 == s2
print(s1)
print(s2)

Is it possible for a key to have multiple names in a dictionary?

I'm not sure if this is even possible but it's worth a shot asking.
I want to be able to access the value from indexing one of the values.
The first thing that came to mind was this but of course, it didn't work.
dict = {['name1', 'name2'] : 'value1'}
print(dict.get('name1))
You can use a tuple (as it's immutable) as a dict key if you need to access it by a pair (or more) of strings (or other immutable values):
>>> d = {}
>>> d[("foo", "bar")] = 6
>>> d[("foo", "baz")] = 8
>>> d
{('foo', 'bar'): 6, ('foo', 'baz'): 8}
>>> d[("foo", "baz")]
8
>>>
This isn't "a key having multiple names", though, it's just a key that happens to be built of multiple strings.
Edit
As discussed in the comments, the end goal is to have multiple keys for each (static) value. That can be succinctly accomplished with an inverted dict first, which is then "flipped" using dict.fromkeys():
def foobar():
pass
def spameggs():
pass
func_to_names = {
foobar: ("foo", "bar", "fb", "foobar"),
spameggs: ("spam", "eggs", "se", "breakfast"),
}
name_to_func = {}
for func, names in func_to_names.items():
name_to_func.update(dict.fromkeys(names, func))
If we tried it you way using:
# Creating a dictionary
myDict = {[1, 2]: 'Names'}
print(myDict)
We get an output of:
TypeError: unhashable type: 'list'
To get around this, we can use this method:
# Creating an empty dictionary
myDict = {}
# Adding list as value
myDict["key1"] = [1, 2]
myDict["key2"] = ["Jim", "Jeff", "Jack"]
print(myDict)

Duplicates in a dictionary (Python)

I need to write a function that returns true if the dictionary has duplicates in it. So pretty much if anything appears in the dictionary more than once, it will return true.
Here is what I have but I am very far off and not sure what to do.
d = {"a", "b", "c"}
def has_duplicates(d):
seen = set()
d={}
for x in d:
if x in seen:
return True
seen.add(x)
return False
print has_duplicates(d)
If you are looking to find duplication in values of the dictionary:
def has_duplicates(d):
return len(d) != len(set(d.values()))
print has_duplicates({'a': 1, 'b': 1, 'c': 2})
Outputs:
True
def has_duplicates(d):
return False
Dictionaries do not contain duplicate keys, ever. Your function, btw., is equivalent to this definition, so it's correct (just a tad long).
If you want to find duplicate values, that's
len(set(d.values())) != len(d)
assuming the values are hashable.
In your code, d = {"a", "b", "c"}, d is a set, not a dictionary.
Neither dictionary keys nor sets can contain duplicates. If you're looking for duplicate values, check if the set of the values has the same size as the dictionary itself:
def has_duplicate_values(d):
return len(set(d.values())) != len(d)
Python dictionaries already have unique keys.
Are you possibly interested in unique values?
set(d.values())
If so, you can check the length of that set to see if it is smaller than the number of values. This works because sets eliminate duplicates from the input, so if the result is smaller than the input, it means some duplicates were found and eliminated.
Not only is your general proposition that dictionaries can have duplicate keys false, but also your implementation is gravely flawed: d={} means that you have lost sight of your input d arg and are processing an empty dictionary!
The only thing that a dictionary can have duplicates of, is values. A dictionary is a key, value store where the keys are unique. In Python, you can create a dictionary like so:
d1 = {k1: v1, k2: v2, k3: v1}
d2 = [k1, v1, k2, v2, k3, v1]
d1 was created using the normal dictionary notation. d2 was created from a list with an even number of elements. Note that both versions have a duplicate value.
If you had a function that returned the number of unique values in a dictionary then you could say something like:
len(d1) != func(d1)
Fortunately, Python makes it easy to do this using sets. Simply converting d1 into a set is not sufficient. Lets make our keys and values real so you can run some code.
v1 = 1; v2 = 2
k1 = "a"; k2 = "b"; k3 = "c"
d1 = {k1: v1, k2: v2, k3: v1}
print len(d1)
s = set(d1)
print s
You will notice that s has three members too and looks like set(['c', 'b', 'a']). That's because a simple conversion only uses the keys in the dict. You want to use the values like so:
s = set(d1.values())
print s
As you can see there are only two elements because the value 1 occurs two times. One way of looking at a set is that it is a list with no duplicate elements. That's what print sees when it prints out a set as a bracketed list. Another way to look at it is as a dict with no values. Like many data processing activities you need to start by selecting the data that you are interested in, and then manipulating it. Start by selecting the values from the dict, then create a set, then count and compare.
This is not a dictionary, is a set:
d = {"a", "b", "c"}
I don't know what are you trying to accomplish but you can't have dictionaries with same key. If you have:
>>> d = {'a': 0, 'b':1}
>>> d['a'] = 2
>>> print d
{'a': 2, 'b': 1}

Destructuring-bind dictionary contents

I am trying to 'destructure' a dictionary and associate values with variables names after its keys. Something like
params = {'a':1,'b':2}
a,b = params.values()
But since dictionaries are not ordered, there is no guarantee that params.values() will return values in the order of (a, b). Is there a nice way to do this?
from operator import itemgetter
params = {'a': 1, 'b': 2}
a, b = itemgetter('a', 'b')(params)
Instead of elaborate lambda functions or dictionary comprehension, may as well use a built in library.
One way to do this with less repetition than Jochen's suggestion is with a helper function. This gives the flexibility to list your variable names in any order and only destructure a subset of what is in the dict:
pluck = lambda dict, *args: (dict[arg] for arg in args)
things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
Also, instead of joaquin's OrderedDict you could sort the keys and get the values. The only catches are you need to specify your variable names in alphabetical order and destructure everything in the dict:
sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))
things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
How come nobody posted the simplest approach?
params = {'a':1,'b':2}
a, b = params['a'], params['b']
Python is only able to "destructure" sequences, not dictionaries. So, to write what you want, you will have to map the needed entries to a proper sequence. As of myself, the closest match I could find is the (not very sexy):
a,b = [d[k] for k in ('a','b')]
This works with generators too:
a,b = (d[k] for k in ('a','b'))
Here is a full example:
>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2
Here's another way to do it similarly to how a destructuring assignment works in JS:
params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
What we did was to unpack the params dictionary into key values (using **) (like in Jochen's answer), then we've taken those values in the lambda signature and assigned them according to the key name - and here's a bonus - we also get a dictionary of whatever is not in the lambda's signature so if you had:
params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
After the lambda has been applied, the rest variable will now contain:
{'c': 3}
Useful for omitting unneeded keys from a dictionary.
Hope this helps.
Maybe you really want to do something like this?
def some_func(a, b):
print a,b
params = {'a':1,'b':2}
some_func(**params) # equiv to some_func(a=1, b=2)
If you are afraid of the issues involved in the use of the locals dictionary and you prefer to follow your original strategy, Ordered Dictionaries from python 2.7 and 3.1 collections.OrderedDicts allows you to recover you dictionary items in the order in which they were first inserted
(Ab)using the import system
The from ... import statement lets us desctructure and bind attribute names of an object. Of course, it only works for objects in the sys.modules dictionary, so one could use a hack like this:
import sys, types
mydict = {'a':1,'b':2}
sys.modules["mydict"] = types.SimpleNamespace(**mydict)
from mydict import a, b
A somewhat more serious hack would be to write a context manager to load and unload the module:
with obj_as_module(mydict, "mydict_module"):
from mydict_module import a, b
By pointing the __getattr__ method of the module directly to the __getitem__ method of the dict, the context manager can also avoid using SimpleNamespace(**mydict).
See this answer for an implementation and some extensions of the idea.
One can also temporarily replace the entire sys.modules dict with the dict of interest, and do import a, b without from.
Warning 1: as stated in the docs, this is not guaranteed to work on all Python implementations:
CPython implementation detail: This function relies on Python stack frame support
in the interpreter, which isn’t guaranteed to exist in all implementations
of Python. If running in an implementation without Python stack frame support
this function returns None.
Warning 2: this function does make the code shorter, but it probably contradicts the Python philosophy of being as explicit as you can. Moreover, it doesn't address the issues pointed out by John Christopher Jones in the comments, although you could make a similar function that works with attributes instead of keys. This is just a demonstration that you can do that if you really want to!
def destructure(dict_):
if not isinstance(dict_, dict):
raise TypeError(f"{dict_} is not a dict")
# the parent frame will contain the information about
# the current line
parent_frame = inspect.currentframe().f_back
# so we extract that line (by default the code context
# only contains the current line)
(line,) = inspect.getframeinfo(parent_frame).code_context
# "hello, key = destructure(my_dict)"
# -> ("hello, key ", "=", " destructure(my_dict)")
lvalues, _equals, _rvalue = line.strip().partition("=")
# -> ["hello", "key"]
keys = [s.strip() for s in lvalues.split(",") if s.strip()]
if missing := [key for key in keys if key not in dict_]:
raise KeyError(*missing)
for key in keys:
yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}
In [6]: hello, key = destructure(my_dict)
In [7]: hello
Out[7]: 'world'
In [8]: key
Out[8]: 'value'
This solution allows you to pick some of the keys, not all, like in JavaScript. It's also safe for user-provided dictionaries
With Python 3.10, you can do:
d = {"a": 1, "b": 2}
match d:
case {"a": a, "b": b}:
print(f"A is {a} and b is {b}")
but it adds two extra levels of indentation, and you still have to repeat the key names.
Look for other answers as this won't cater to the unexpected order in the dictionary. will update this with a correct version sometime soon.
try this
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
keys = data.keys()
a,b,c = [data[k] for k in keys]
result:
a == 'Apple'
b == 'Banana'
c == 'Carrot'
Well, if you want these in a class you can always do this:
class AttributeDict(dict):
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__.update(self)
d = AttributeDict(a=1, b=2)
Based on #ShawnFumo answer I came up with this:
def destruct(dict): return (t[1] for t in sorted(dict.items()))
d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)
(Notice the order of items in dict)
An old topic, but I found this to be a useful method:
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
for key in data.keys():
locals()[key] = data[key]
This method loops over every key in your dictionary and sets a variable to that name and then assigns the value from the associated key to this new variable.
Testing:
print(a)
print(b)
print(c)
Output
Apple
Banana
Carrot
An easy and simple way to destruct dict in python:
params = {"a": 1, "b": 2}
a, b = [params[key] for key in ("a", "b")]
print(a, b)
# Output:
# 1 2
I don't know whether it's good style, but
locals().update(params)
will do the trick. You then have a, b and whatever was in your params dict available as corresponding local variables.
Since dictionaries are guaranteed to keep their insertion order in Python >= 3.7, that means that it's complete safe and idiomatic to just do this nowadays:
params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)
Output:
1
2

Categories

Resources