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.
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
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.
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')
''
Suppose that I have dictionary with 3 keys 'x', 'y' and 'z'. What I need to do is to write a function that, given 'x' as argument, swaps the values stored in 'y' and 'z'.
def swap(d, key):
a, b = [_ for _ in d if _ != key]
d[a], d[b] = d[b], d[a]
This is what I've came up with, but I'm looking for a more simple and concise way. Is there any, as far as you know?
You can use a slightly more clever means of determining the keys to swap by doing:
a, b = d.keys() - {key} # On Py3; on Python 2.7, you'd use d.viewkeys()
but it's a pretty minor "improvement"; using set operations moves more work to the C layer, avoiding the Python layer iteration of a list comprehension, but the difference when you're talking about iterating three values is pretty trivial.
It's using a KeysView (a live, set-like view of the dict's keys) to get set operations to preserve the two keys not passed.
I'd do this this way, to avoid using loop:
def swap(dictS, key):
keys = list(dictS.keys())
keys.remove(key)
dictS[keys[0]], dict[keys[1]] = dictS[keys[1]], dict[keys[0]]
To solve this, we first need to find the two keys that are not same as the input key. We then just swap the values for those keys.
def swap(d, key):
keys_to_swap = []
for k in d:
if k != key:
keys_to_swap.append(k)
# keys_to_swap are the keys that need to be swapped
# Swap now
temp = d.get(keys_to_swap[0])
d[keys_to_swap[0]] = d.get(keys_to_swap[1])
d[keys_to_swap[1]] = temp
return d
Your original answer is correct, but you are not returning d.
So to correct your solution:
def swap2(d, key):
a, b = [_ for _ in d if _ != key]
d[a], d[b] = d[b], d[a]
return d #Added this line
I have a multi dimensional list:
multiDimList = [['a',1],['a',1],['a',1],['b',2],['c',3],['c',3]]
I'm trying to sum the instances of element [1] where element [0] is common.
To put it more clearly, my desired output is another multi dimensional list:
multiDimListSum = [['a',3],['b',2],['c',6]]
I see I can access, say the value '2' in multiDimList by
x = multiDimList [3][1]
so I can grab the individual elements, and could probably build some sort of function to do this job, but it'd would be disgusting.
Does anyone have a suggestion of how to do this pythonically?
Assuming your actual sequence has similar elements grouped together as in your example (all instances of 'a', 'b' etc. together), you can use itertools.groupby() and operator.itemgetter():
from itertools import groupby
from operator import itemgetter
[[k, sum(v[1] for v in g)] for k, g in groupby(multiDimList, itemgetter(0))]
# result: [['a', 3], ['b', 2], ['c', 6]]
Zero Piraeus's answer covers the case when field entries are grouped in order. If they're not, then the following is short and reasonably efficient.
from collections import Counter
reduce(lambda c,x: c.update({x[0]: x[1]}) or c, multiDimList, Counter())
This returns a collection, accessible by element name. If you prefer it as a list you can call the .items() method on it, but note that the order of the labels in the output may be different from the order in the input even in the cases where the input was consistently ordered.
You could use a dict to accumulate the total associated to each string
d = {}
multiDimList = [['a',1],['a',1],['a',1],['b',2],['c',3],['c',3]]
for string, value in multiDimList:
# Retrieves the current value in the dict if it exists or 0
current_value = d.get(string, 0)
d[string] += value
print d # {'a': 3, 'b': 2, 'c': 6}
You can then access the value for b by using d["b"].