Given a list of items, and a map from a predicate function to the "value" function, the code below applies "value" functions to the items satisfying the corresponding predicates:
my_re0 = re.compile(r'^([a-z]+)$')
my_re1 = re.compile(r'^([0-9]+)$')
my_map = [
(my_re0.search, lambda x: x),
(my_re1.search, lambda x: x),
]
for x in ['abc','123','a1']:
for p, f in my_map:
v = p(x)
if v:
print f(v.groups())
break
Is there a way to express the same with a single statement?
If I did not have to pass the value returned by the predicate to the "value" function then I could do
for x in ['abc','123','a1']:
print next((f(x) for p, f in my_map if p(x)), None)
Can something similar be done for the code above? I know, maybe it is better to leave these nested for loops, but I am just curious whether it is possible.
A bit less terse than Nate's ;-)
from itertools import product
comb = product(my_map, ['abc','123','a1'])
mapped = ((p(x),f) for (p,f),x in comb)
groups = (f(v.groups()) for v,f in mapped if v)
print next(groups), list(groups) # first match and the rest of them
[f(v.groups()) for x in ['abc','123','a1'] for p, f in my_map for v in [p(x)] if v]
You said more terse, right? ;^)
here is my version:
for x in ['abc','123','a1']:
print next((f(v.groups()) for p, f in my_map for v in [p(x)] if v), None)
this version does not iterate over the whole my_map but stops as soon as the first successful mapping is found.
Related
I currently have the code below working fine:
Can someone help me solve the collision created from having two keys with the same number in the dictionary?
I tried multiple approach (not listed here) to try create an array to handle it but my approaches are still unsuccessful.
I am using #python3.7
def find_key(dic1, n):
'''
Return the key '3' from the dict
below.
'''
d = {}
for x, y in dic1.items():
# swap keys and values
# and update the result to 'd'
d[y] = x
try:
if n in d:
return d[y]
except Exception as e:
return (e)
dic1 = {'james':2,'david':3}
# Case to test that return ‘collision’
# comment 'dic1' above and replace it by
# dic1 below to create a 'collision'
# dic1 = {'james':2,'david':3, 'sandra':3}
n = 3
print(find_key(dic1,n))
Any help would be much appreciated.
You know there should be multiple returns, so plan for that in advance.
def find_keys_for_value(d, value):
for k, v in d.items():
if v == value:
yield k
data = {'james': 2, 'david': 3, 'sandra':3}
for result in find_keys_for_value(data, 3):
print (result)
You can use a defaultdict:
from collections import defaultdict
def find_key(dct, n):
dd = defaultdict(list)
for x, y in dct.items():
dd[y].append(x)
return dd[n]
dic1 = {'james':2, 'david':3, 'sandra':3}
print(find_key(dic1, 3))
print(find_key(dic1, 2))
print(find_key(dic1, 1))
Output:
['david', 'sandra']
['james']
[]
Building a defaultdict from all keys and values is only justified if you will repeatedly search for keys of the same dict given different values, though. Otherwise, the approach of Kenny Ostrom is preferrable. In any case, the above makes little sense if left as it stands.
If you are not at ease with generators and yield, here is the approach of Kenny Ostrom translated to lists (less efficient than generators, better than the above for one-shot searches):
def find_key(dct, n):
return [x for x, y in dct.items() if y == n]
The output is the same as above.
I can sort an array by mapping. But how can i build the lambda for a unknown count of dynamic maps in s?
l = ['1','2','3']
s = [{ '3':'a', '2':'a', '1':'c'},{ '3':'z', '2':'a', '1':'b'},{ '1':34, '2':123, '3':1000}]
sorted(l, key=lambda x: (s[0][x], s[1][x], s[2][x]))
yes, why hardcoding this when you can iterate on s and create the key using list comprehension:
sorted(l, key=lambda x: [p[x] for p in s])
(note that now key is a list, no longer a tuple but that doesn't matter since they share the same ordering)
You can sort in an arbitrary number of parameters using comparator like so:
from functools import cmp_to_key
l = ['1','2','3']
s = [{ '3':'a', '2':'a', '1':'c'},{ '3':'z', '2':'a', '1':'b'},{ '1':34, '2':123, '3':1000}]
def compare(x,y):
for dc in s:
if dc[x] < dc[y]:
return -1
elif dc[x] > dc[y]:
return 1
return 0
sorted(l, key= cmp_to_key(compare))
There's no need to use lambda function for this one.
I have a list as below
tlist=[(‘abc’,HYD,’user1’), (‘xyz’,’SNG’,’user2’), (‘pppp’,’US’,’user3’), (‘qq’,’HK’,’user4’)]
I want to display the second field tuple of provided first field of tuple.
Ex:
tlist(‘xyz’)
SNG
Is there way to get it?
A tuple doesn't have a hash table lookup like a dictionary, so you will need to loop through it in sequence until you find it:
def find_in_tuple(tlist, search_term):
for x, y, z in tlist:
if x == search_term:
return y
print(find_in_tuple(tlist, 'xyz')) # prints 'SNG'
If you plan to do this multiple times, you definitely want to convert to a dictionary. I would recommend making the first element of the tuple the key and then the other two the values for that key. You can do this very easily using a dictionary comprehension.
>>> tlist_dict = { k: (x, y) for k, x, y in tlist } # Python 3: { k: v for k, *v in tlist }
>>> tlist_dict
{'qq': ['HK', 'user4'], 'xyz': ['SNG', 'user2'], 'abc': ['HYD', 'user1'], 'pppp': ['US', 'user3']}
You can then select the second element as follows:
>>> tlist_dict['xyz'][0]
'SNG'
If there would be multiple tuples with xyz as a first item, use the following simple approach(with modified example):
tlist = [('abc','HYD','user1'), ('xyz','SNG','user2'), ('pppp','US','user3'), ('xyz','HK','user4')]
second_fields = [f[1] for f in tlist if f[0] == 'xyz']
print(second_fields) # ['SNG', 'HK']
I have a dictionary with a tuple of 5 values as a key. For example:
D[i,j,k,g,h] = value.
Now i need to process all elements with a certain partial key pair (i1,g1):
I need now for each pair (i1,g1) all values that have i == i1 and g == g1 in the full key.
What is an pythonic and efficient way to retrieve this, knowing that i need the elements for all pairs and each full key belongs to exactly one partial key?
Is there a more appropriate data structure than dictionaries?
One reference implementation is this:
results = {}
for i in I:
for g in G:
results[i,g] = []
for i,j,k,g,h in D:
if i1 == i and g1 == g:
results[i,g].append(D[i,j,k,g,h])
Assuming you know all the valid values for the different indices you can get all possible keys using itertools.product:
import itertools
I = [3,6,9]
J = range(10)
K = "abcde"
G = ["first","second"]
H = range(10,20)
for tup in itertools.product(I,J,K,G,H):
my_dict[tup] = 0
To restrict the indices generated just put a limit on one / several of the indices that gets generated, for instance all of the keys where i = 6 would be:
itertools.product((6,), J,K,G,H)
A function to let you specify you want all the indices where i==6 and g =="first" would look like this:
def partial_indices(i_vals=I, j_vals=J, k_vals=K, g_vals = G, h_vals = H):
return itertools.product(i_vals, j_vals, k_vals, g_vals, h_vals)
partial_indices(i_vals=(6,), g_vals=("first",))
Or assuming that not all of these are present in the dictionary you can also pass the dictionary as an argument and check for membership before generating the keys:
def items_with_partial_indices(d, i_vals=I, j_vals=J, k_vals=K, g_vals = G, h_vals = H):
for tup in itertools.product(i_vals, j_vals, k_vals, g_vals, h_vals):
try:
yield tup, d[tup]
except KeyError:
pass
for k,v in D.iteritems():
if i in k and p in k:
print v
Suppose we have the following set, S, and the value v:
S = {(0,1),(2,3),(4,5)}
v = 3
I want to test if v is the second element of any of the pairs within the set. My current approach is:
for _, y in S:
if y == v:
return True
return False
I don't really like this, as I have to put it in a separate function and something is telling me there's probably a nicer way to do it. Can anyone shed some light?
The any function is tailor-made for this:
any( y == v for (_, y) in S )
If you have a large set that doesn't change often, you might want to project the y values onto a set.
yy = set( y for (_, y) in S )
v in yy
Of course, this is only of benefit if you compute yy once after S changes, not before every membership test.
You can't do an O(1) lookup, so you don't get much benefit from having a set. You might consider building a second set, especially if you'll be doing lots of lookups.
S = {(0,1), (2,3), (4,5)}
T = {x[1] for x in S}
v = 3
if v in T:
# do something
Trivial answer is any (see Marcelo's answer).
Alternative is zip.
>>> zip(*S)
[(4, 0, 2), (5, 1, 3)]
>>> v in zip(*S)[1]
True