How can I test whether any of a or b or c are keys in a dict?
Are there some short and simple methods in Python?
With two keys, I can use
if a or b in my_dict.keys():
How can I do the same with three keys?
You may use any() to check any of the condition is True. Let say your dict is:
my_dict = {
'x': 1,
'y': 2,
'a': 3 # `a` key in dict
}
In order to check any item exists in dictionary's keys, you may do:
>>> values_to_check = ['a', 'b', 'c'] # list of value to check
# v You do not need `.keys()` as by default `in` checks in keys
>>> any(item in my_dict for item in values_to_check)
True
Warning: if a or b in my_dict.keys(): doesn't do what you think. For example:
>>> 'a' or 'b' in {'c': 'd', 'e': 'f'}.keys()
'a'
What actually happens there is the result of short-circuit evaluation: first, 'a' is evaluated, and if it's "truthy", it's returned without checking anything else. Only if 'a' were "falsy" would 'b' (and only 'b') be checked against the dict's keys.
It's a little clearer what Python actually does if you add some parentheses to your code:
if (a) or (b in my_dict.keys()):
The way you'd actually have to write the two-value version of the code in that style would be:
if a in my_dict.keys() or b in my_dict.keys():
# ...
On to your actual question:
To test an arbitrary number of values (including two), you have two choices:
if any(x in my_dict.keys() for x in (a, b, c)):
# ...
or
if my_dict.keys() & {a, b, c}: # replace keys with viewkeys in Python 2
# ...
Which one of those you go for is largely down to personal taste ... although the any(...) solution is necessary if you require actual True or False results, rather than "truthy" values that work in an if statement.
Related
If I have a dictionary where each value is a list, how can I check if there is a specific element in my list? For example:
myDict = { 0 : ['a','b','c'],
1 : ['d','e','f']}
How can I check if 'a' exists?
You can use any:
any('a' in lst for lst in myDict.values())
This will stop the iteration and evaluate to True on the first find. any is the built-in short-cut for the following pattern:
for x in y:
if condition:
return True
return False
# return any(condition for x in y)
It always strikes me as strange when someone wants to scan the values of a dictionary. It's highly unefficient if done many times.
Instead, I'd build another dictionary, or a set for quick check:
myDict = { 0 : ['a','b','c'],
1 : ['d','e','f']}
rset = {x for v in myDict.values() for x in v}
print(rset)
gives:
{'b', 'e', 'c', 'd', 'a', 'f'}
now:
'a' in rset
is super fast and concise. Build as many sets & dictionaries as you need on your original data set to get a fast lookup.
Check all values
We can use itertools.chain and use it in a rather self-explaining one liner:
from itertools import chain
if 'a' in chain.from_iterable(myDict.values()):
# do something
pass
Here we will chain the .values() of a list together in an iterable, and thus check membership of 'a'.
Note that this runs in linear time with the total number of values in the lists. In case you have to perform the membership check a single time, we can not do much about it, but in case we have to check it multiple times, it is better to cache the values in a set (given the values are hashable).
Check a specific key
In case you want to check a specific key, we can just lookup the corresponding value and check membership:
if 'a' in myDict[0]:
# do something
pass
In case it is not certain if the key is present in myDict, and we want to return False in that case, we can use .get(..) and use () (the empty tuple) as a fallback value:
# will not error, but False in case key does not exists
if 'a' in myDict.get(0, ()):
# do something
pass
I have several sets of values, and need to check in which of some of them a given value is located, and return the name of that set.
value = 'a'
set_1 = {'a', 'b', 'c'}
set_2 = {'d', 'e', 'f'}
set_3 = {'g', 'h', 'i'}
set_4 = {'a', 'e', 'i'}
I'd like to check if value exists in sets 1-3, without including set_4 in the method, and return the set name. So something like:
find_set(value in set_1, set_2, set_3)
should return
set_1
Maybe some neat lambda function? I tried
w = next(n for n,v in filter(lambda t: isinstance(t[1],set), globals().items()) if value in v)
from Find if value exists in multiple lists but that approach checks ALL local/global sets. That won't work here, because the value can exist in several of them. I need to be able to specify in which sets to look.
Don't use an ugly hackish lambda which digs in globals so you can get a name; that will confuse anyone reading your code including yourself after a few weeks :-).
You want to be able to get a name for sets you have defined, well, this is why we have dictionaries. Make a dictionary out of your sets and then you can create handy/readable set/list comprehensions to get what you want in a compact readable fashion:
>>> d = {'set_1': set_1, 'set_2': set_2, 'set_3': set_3, 'set_4': set_4}
To catch all sets in which 'a' is located:
>>> {name for name, items in d.items() if 'a' in items}
{'set_1', 'set_4'}
To exclude some name add another the required clause to the if for filtering:
>>> {name for name, items in d.items() if 'a' in items and name != 'set_4'}
{'set_1'}
You can of course factor this into a function and be happy you'll be able to understand it if you bump into it in the future:
def find_sets(val, *excludes, d=d):
return {n for n, i in d.items() if val in i and n not in excludes}
This behaves in a similar way as the previous. d=d is probably not the way you want to do it, you'll probably be better of using some **d syntax for this.
If you just want to get the first value, return the next(comprehension) from your function like this:
def find_sets(val, *excludes, d=d):
return next((n for n, i in d.items() if val in i and n not in excludes), '')
The '' just indicates a default value to be returned if no elements are actually found, that is, when called with a value that isn't present, an empty string will be returned (subject to change according to your preferences):
>>> find_sets('1')
''
I wonder if it is possible to gain the same output as from this code:
d = {'a':None,'b':'12345','c':None}
nones=False
for k,v in d.items():
if d[k] is None:
nones=True
or
any([v==None for v in d.values()])
but without a for loop iterator, or generator?
You can use
nones = not all(d.values())
If all values are not None, nones would be set to False, else True. It is just an abstraction though, internally it must iterate over values list.
You could have Python do the looping in C code by using a dictionary view; this does a membership test against all values without creating a new list:
if None not in d.values():
(In Python 2, use dict.viewvalues() to get a dictionary view, as dict.values() returns a list there).
Demo on Python 3:
>>> d = {'a': None, 'c': None, 'b': '12345'}
>>> None not in d.values()
False
This will loop over the values until a match is found, just like list membership or a proper any() test, making this an O(N) test. This differs from a dictionary or set membership test, where hashing can be used to give you a fixed cost test on average.
You were not using any() properly; drop the [...] brackets:
if any(v is not None for v in d.values()): # Python 2: use d.itervalues() or d.viewvalues()
If your goal is to test for multiple values, and you need to avoid constant looping for each test, consider creating an inverse index instead:
inverse_index = {}
for key, value in d.items():
inverse.setdefault(value, set()).add(key)
This requires the values to be hashable, however. You can now simply test for each value:
if None not in inverse_index:
in O(1) time.
There is a dictionary that may include keys starting from 0 and values: a, b, c, d, e. Each time the values may be assigned to different keys keys. Size of the dictionary may change as well.
I am interested in two values. Let's call them b and d.
Is there any algorithm that determine situations when b appears earlier than d (i.e. b's key is smaller than d's) and when d appears earlier than b (i.e. d's key is is smaller than b's)?
A dictionary has no order. So your wording "b's key is smaller than d's" is the right one.
Now, it looks like you could swap keys and values...
If the values are hashable then you could generate a reverse dictionary and check the values. Otherwise, you'll need to brute-force it.
def dictfind(din, tsent, fsent):
for k in sorted(din.iterkeys()):
if din[k] == tsent:
return True
if din[k] == fsent:
return False
else:
raise ValueError('No match found')
D = {0:'a', 1:'b', 2:'c', 3:'d', 4:'e'}
print dictfind(D, 'b', 'd')
Dictionaries are unordered sets of key-value pairs. dict.keys() need not produce the same output always. Can't you do what you want with lists?
First create your dictionary
>>> import random
>>> keys = range(5)
>>> random.shuffle(keys)
>>> d=dict(zip(keys, "abcde"))
>>> d
{0: 'd', 1: 'c', 2: 'e', 3: 'b', 4: 'a'}
Now create a dictionary using the keys of d as the values and the values of d as the keys
>>> rev_d = dict((v,k) for k,v in d.items())
Your comparisons are now just regular dictionary lookups
>>> rev_d['b'] > rev_d['d']
True
From your comment on gnibbler's answer, it sounds like when there are multiple occurrences of a value, you only care about the earliest appearing one. In that case, the swapped (value, key)-dictionary suggested can still be used, but with minor modification to how you build it.
xs = {0: 'a', 1: 'b', 2: 'a'}
ys = {}
for k, v in xs.iteritems():
if v not in ys or k < ys[v]:
ys[v] = k
You could then define a function that tells you which of two values maps to a smaller index:
def earlier(index_map, a, b):
"""Returns `a` or `b` depending on which has a smaller value in `index_map`.
Returns `None` if either `a` or `b` is not in `index_map`.
"""
if a not in index_map or b not in index_map:
return None
if index_map[a] < index_map[b]:
return a
return b
Usage:
print earlier(ys, 'a', 'b')
There are some subtleties here whose resolution depends on your particular problem.
What should happen if a or b is not in index_map? Right now we return None.
What should happen if index_map[a] == index_map[b]? From your comments it sounds like this may not happen in your case, but you should consider it. Right now we return b.
I'm going through a whole bunch of tuples with a many-to-many correlation, and I want to make a dictionary where each b of (a,b) has a list of all the a's that correspond to a b. It seems awkward to test for a list at key b in the dictionary, then look for an a, then append a if it's not already there, every single time through the tuple digesting loop; but I haven't found a better way yet. Does one exist? Is there some other way to do this that's a lot prettier?
See the docs for the setdefault() method:
setdefault(key[, default])
If key is
in the dictionary, return its value.
If not, insert key with a value of
default and return default. default
defaults to None.
You can use this as a single call that will get b if it exists, or set b to an empty list if it doesn't already exist - and either way, return b:
>>> key = 'b'
>>> val = 'a'
>>> print d
{}
>>> d.setdefault(key, []).append(val)
>>> print d
{'b': ['a']}
>>> d.setdefault(key, []).append('zee')
>>> print d
{'b': ['a', 'zee']}
Combine this with a simple "not in" check and you've done what you're after in three lines:
>>> b = d.setdefault('b', [])
>>> if val not in b:
... b.append(val)
...
>>> print d
{'b': ['a', 'zee', 'c']}
Assuming you're not really tied to lists, defaultdict and set are quite handy.
import collections
d = collections.defaultdict(set)
for a, b in mappings:
d[b].add(a)
If you really want lists instead of sets, you could follow this with a
for k, v in d.iteritems():
d[k] = list(v)
And if you really want a dict instead of a defaultdict, you can say
d = dict(d)
I don't really see any reason you'd want to, though.
Use collections.defaultdict
your_dict = defaultdict(list)
for (a,b) in your_list:
your_dict[b].append(a)
you can sort your tuples O(n log n) then create your dictionary O(n)
or simplier O(n) but could impose heavy load on memory in case of many tuples:
your_dict = {}
for (a,b) in your_list:
if b in your_dict:
your_dict[b].append(a)
else:
your_dict[b]=[a]
Hmm it's pretty much the same as you've described. What's awkward about that?
You could also consider using an sql database to do the dirty work.
Instead of using an if, AFAIK it is more pythonic to use a try block instead.
your_list=[('a',1),('a',3),('b',1),('f',1),('a',2),('z',1)]
your_dict={}
for (a,b) in your_list:
try:
your_dict[b].append(a)
except KeyError:
your_dict[b]=[a]
print your_dict
I am not sure how you will get out of the key test, but once they key/value pair has been initialized it is easy :)
d = {}
if 'b' not in d:
d['b'] = set()
d['b'].add('a')
The set will ensure that only 1 of 'a' is in the collection. You need to do the initial 'b' check though to make sure the key/value exist.
Dict get method?
It returns the value of my_dict[some_key] if some_key is in the dictionary, and if not - returns some default value ([] in the example below):
my_dict[some_key] = my_dict.get(some_key, []).append(something_else)
There's another way that's rather efficient (though maybe not as efficient as sets) and simple. It's similar in practice to defaultdict but does not require an additional import.
Granted that you have a dict with empty (None) keys, it means you also create the dict keys somewhere. You can do so with the dict.fromkeys method, and this method also allows for setting a default value to all keys.
keylist = ['key1', 'key2']
result = dict.fromkeys(keylist, [])
where result will be:
{'key1': [], 'key2': []}
Then you can do your loop and use result['key1'].append(..) directly