I have a two list of tuples
t1 = [ ('a',3,4), ('b',3,4), ('c',4,5) ]
t2 = [ ('a',4,6), ('c',3,4), ('b',3,6), ('d',4,5) ]
Such that
the order of the tuples may not be the same order and
the lists may not contain the same amount of tuple elements.
My goal is to compare the two lists such that if the string element matches, then compare the last integer element in the tuple and return a list containing -1 if t1[2] < t2[2], 0 if they are equal and 1 if they are greater than.
I've tried different variations but the problem i have is finding a way to match the strings to do proper comparison.
return [diff_unique(x[2],y[2]) for x,y in zip(new_list,old_list) ]
Where diff_unique does the aforementioned comparison of the integers, and new_list is t1 and old_list is t2.
I've also tried this:
return [diff_unique(x[2],y[2]) for x,y in zip(new_list,old_list) if(x[0]==y[0]]
What I intend to do is use the returned list and create a new four-tuple list with the original t1 values along with the difference from the matching t2 tuple. i.e
inc_dec_list = compare_list(new,old)
final_list = [ (f,r,u,chge) for (f,r,u), chge in zip(new,inc_dec_list)]
Where new = t1 and old = t2. This may have been an important detail, sorry I missed it.
Any help in the right direction?
Edit: I have added my test case program that mimicks what my original intent is for those who want to help. Thank you all.
import os
import sys
old = [('a',10,1),('b',10,2),('c',100,4),('d',200,4),('f',45,2)]
new = [('a',10,2),('c',10,2),('b',100,2),('d',200,6),('e',233,4),('g',45,66)]
def diff_unique(a,b):
print "a:{} = b:{}".format(a,b)
if a < b:
return -1
elif a==b:
return 0
else:
return 1
def compare_list(new_list, old_list):
a = { t[0]:t[1:] for t in new_list }
b = { t[0]:t[1:] for t in old_list }
common = list( set(a.keys())&set(b.keys()))
return [diff_unique(a[key][1], b[key][1]) for key in common]
#get common tuples
#common = [x for x,y in zip(new_list,old_list) if x[0] == y[0] ]
#compare common to old list
#return [diff_unique(x[2],y[2]) for x,y in zip(new_list,old_list) ]
inc_dec_list = compare_list(new,old)
print inc_dec_list
final_list = [ (f,r,u,chge) for (f,r,u), chge in zip(new,inc_dec_list)]
print final_list
To match the tuples by string from different lists, you can use dict comprehension (order inside the tuples is preserved):
a = {t[0]:t[1:] for t in t1} # {'a': (3, 4), 'c': (4, 5), 'b': (3, 4)}
b = {t[0]:t[1:] for t in t1} # {'a': (4, 6), 'c': (3, 4), 'b': (3, 6), 'd': (4, 5)}
Then you can iterate over the keys of both dictionaries and do the comparison. Assuming you only want to do the comparison for keys/tuples present in t1 and t2, you can join the keys using sets:
common_keys = list(set(a.keys())&set(b.keys()))
And finally compare the dictionary's items and create the list you want like this:
return [diff_unique(a[key][1],b[key][1]) for key in common_keys ]
If you need the output in the order of the alphabetically sorted characters, use the sorted function on the keys:
return [diff_unique(a[key][1],b[key][1]) for key in sorted(common_keys) ]
If you want all keys to be considered, you can do the following:
all_keys = list(set(a.keys()+b.keys()))
l = list()
for key in sorted(all_keys):
try:
l.append(diff_unique(a[key][1],b[key][1]))
except KeyError:
l.append("whatever you want")
return l
With the new information about what values should be returned in what order, the solution would be this:
ordered_keys = [t[0] for t in t1]
a = {t[0]:t[1:] for t in t1} # {'a': (3, 4), 'c': (4, 5), 'b': (3, 4)}
b = {t[0]:t[1:] for t in t1} # {'a': (4, 6), 'c': (3, 4), 'b': (3, 6), 'd': (4, 5)}
l = list()
for key in sorted(ordered_keys):
try:
l.append(diff_unique(a[key][1],b[key][1]))
except KeyError:
l.append(0) # default value
return l
First, build a default dictionary from each list, with the default value for a nonexistent key being a tuple whose last element is the smallest possible value for a comparison.
SMALL = (-float['inf'],)
from collections import defaultdict
d1 = defaultdict(lambda: SMALL, [(t[0], t[1:]) for t in t1])
d2 = defaultdict(lambda: SMALL, [(t[0], t[1:]) for t in t2])
Next, iterate over the keys in each dictionary (which can be created easily with itertools.chain). You probably want to sort the keys for the resulting list to have any meaning (otherwise, how do you know which keys produced which of -1/0/1?)
from itertools import chain
all_keys = set(chain(d1, d2))
result = [cmp(d1[k][-1], d2[k][-1]) for k in sorted(all_keys)]
Here is a simple solution of your problem,
It is not one line as you tried. I hope it will still help you
for a in t1:
for b in t2:
if a[0] != b[0]:
continue
return cmp(a[-1], b[-1])
In python 3.x, you can compare two lists of tuples
a and b thus:
import operator
a = [(1,2),(3,4)]
b = [(3,4),(1,2)]
# convert both lists to sets before calling the eq function
print(operator.eq(set(a),set(b))) #True
Related
I have a list of 4 dictionaries. I also have a list of 36 values. I'm trying to loop through the list of dictionaries, filling each dictionary with the list values in ascending order having item 1 as the key, 2 & 3 as the value attached and so on. The final result being 4 dictionaries, each with three keys and each key having two values attached.
The end result would be
dict1={x:(7,4),y:(7,8),z:(7:22)}
dict2={x: (111,4),y:(111,8),z:(111:22)}
...
Currently I have the following which does not work.
my_list = ['x',7,4,'y',7,8,'z',7,22,'x',111,4,'y',111,8,'z',111,22 and so on]
dict1={}
dict2={}
dict3={}
dict4={}
my_dict_list=[dict1,dict2,dict3,dict4]
for dicts in my_dict_list:
for x in range (0,len(my_list),3):
dicts[my_list[x]] = my_list[x+1],my_list[x+2]
break
the output of that code being the first 3 items in my list, in all four of the dictionaries. As so:
>>> dict_1
{'x': (7, 4)}
>>> dict_2
{'x': (7, 4)}
>>> dict_3
{'x': (7, 4)}
>>> dict_4
{'x': (7, 4)}
I think this is the closest I've got so far as it is actually filling each dictionary, previously I've only managed to fill the first dictionary and other similar wrong scenarios. Can anyone help or point me in the right direction?
Try this:
my_list = ['x',7,4,'y',7,8,'z',7,22,'x',111,4,'y',111,8,'z',111,22]
my_dict_list = []
idx = 0
current_dict = {}
while idx < len(my_list):
key, *vals = my_list[idx : idx + 3]
# If key repeats, starting a new current_dict, old one adding to my_dict_list
if key in current_dict:
my_dict_list.append(current_dict)
current_dict = {}
current_dict[key] = vals
idx += 3
if current_dict:
my_dict_list.append(current_dict)
print(my_dict_list)
In the solution above we loop my_list reading 3 values at a time. And in case if key of a dictionary is repeated this is interpreted and a starting of a new dictionary so the old one must be stored in my_dict_list.
I'm a new programmer. I need help with my code. In code, I have a dictionary that its values are a list. I want to do something in 'while' until the dictionary has a value. I used while(len(migration))!= 0, but it's not correct. How can I iterate while for all values in a dictionary?
At the end of while, I want to delete values in the dictionary, but I don't know how can I do this and update my dictionary's values.
migration_p = {2: [3, 4], 3: [3]}
def q_sequence():
global p, sequence_q, r_s, d_s, d, partition, migration_p
while len(migration_p) != 0:
sequence_qi = []
migration_pi = copy.deepcopy(migration_p)
while len(migration_pi) != 0:
tqi_min = math.inf
for p in migration_pi.keys():
# if len(migration_pi[p]) < M - A:
for d in migration_pi[p]:
for r_src in migration_pi.keys():
if r_src not in sequence_qi:
xx = time_qi(d, p)[0]
if xx < tqi_min:
tqi_min = xx
r_s = r_src
d_s = d
sequence_qi.append([(time_qi(d, p)[1], d_s), r_s])
migration_pi = {p: d for p, d in migration_pi.items() if p != r_s}
migration_pi = {p: d for p, d in migration_pi.items() if (p != r_s or d != d_s)}
sequence_q.append(sequence_qi)
# print(sequence_q)
break
return sequence_q
I expect the output to be [[[(1, 3), 2], [(1, 3), 3]]] but the actual output is [[[(1, 3), 2], [(1, 3), 3], [(2, 4), 2]]].
To iterate on values
for value in dictionary.values():
...
do this to retrieve pair of key and value
for key, value in yourDictionary:
print "this is key : "+key
print "this is value : "+value
for key in migration.keys():
print "Key: " + key
print "Value " + migration.pop(key) # This will delete the key from your dict and return a value
migration[key] = new_value # This will create the key again and update it's value
Would it help to know there's a built in way to iterate over all entries in a dictionary?
I can't remember exactly, but it is something to the effect of
for key in nameOfDictionary:
print(nameOfDictionary[key])
Then if after you did all of your iterative stuff, you could make it delete the dictionary by doing something like this.
for key in nameOfDictionary:
print(nameOfDictionary[key])
del nameOfDictionary
Optionally, if you want the dictionary to still exist, you can use nameOfDictionary.clear to simply remove all entries.
I have a dict of tuples such as:
d = {'a': (3, 5), 'b': (5, 8), 'c': (9, 3)}
I want to return the key of the minimum of the tuple values based on the tuple index. For example, if using tuple index = 0, then 'a' would be returned. if index = 1, then 'c' would be returned. I have tried using min(), for example
min(d, key=d.get)
but am not sure how to manipulate it to select the tuple index to use. Although there are similar questions, I have not found an answer to this. Apologies in advance if this is a duped question, and please link to the answer. Thanks
You can write a lambda function to get the elements from the value by their index:
min(d, key=lambda k: d[k][0])
# 'a'
min(d, key=lambda k: d[k][1])
# 'c'
Since multiple keys could have the same value, you might want to return a list of matching keys, not just a single key.
def min_keys(d, index):
# Initialize lists
values = []
matches = []
# Append tuple items to list based on index
for t in list(d.values()):
values.append(t[index])
# If the item matches the min, append the key to list
for key in d:
if d[key][index] == min(values):
matches.append(key)
# Return a list of all keys with min value at index
return matches
Dictionaries are unsorted and have no index.
If you want the return the key alphabetically first you could use the ascii order:
print(chr(min([ord(key) for key in d.keys()])))
Here's a portable method you can use for dicts with a structure like yours, and feel free to choose the index of interest in the tuple:
def extract_min_key_by_index(cache, index):
min_val = float('inf')
min_key = 0
for k, v in d.iteritems():
if v[index] < min_val:
min_key, min_val = k, v[index]
return min_key
d = {'a': (3, 5), 'b': (5, 8), 'c': (9, 3)}
INDEX = 0
print extract_min_key_by_index(d, INDEX)
This question already has answers here:
Pythonic way to avoid "if x: return x" statements
(18 answers)
Closed 6 years ago.
There is a dict (say d). dict.get(key, None) returns None if key doesn't exist in d.
How do I get the first value (i.e., d[key] is not None) from a list of keys (some of them might not exist in d)?
This post, Pythonic way to avoid “if x: return x” statements, provides a concrete way.
for d in list_dicts:
for key in keys:
if key in d:
print(d[key])
break
I use xor operator to acheive it in one line, as demonstrated in,
# a list of dicts
list_dicts = [ {'level0' : (1, 2), 'col': '#ff310021'},
{'level1' : (3, 4), 'col': '#ff310011'},
{'level2' : (5, 6), 'col': '#ff312221'}]
# loop over the list of dicts dicts, extract the tuple value whose key is like level*
for d in list_dicts:
t = d.get('level0', None) or d.get('level1', None) or d.get('level2', None)
col = d['col']
do_something(t, col)
It works. In this way, I just simply list all options (level0 ~ level3). Is there a better way for a lot of keys (say, from level0 to level100), like list comprehensions?
This line:
x, y = d.get('level0', None) or d.get('level1', None) or d.get('level2', None)
Is basically mapping a list of ['level0', 'level1', 'level2'] to d.get (None is already the default value; there's no need to explicitly state it in this case). Next, you want to choose the one that doesn't map to None, which is basically a filter. You can use the map() and filter() built-in functions (which are lazy generator-like objects in Python 3) and call next() to get the first match:
list_dicts = [ {'level0' : (1, 2), 'col': '#ff310021'},
{'level1' : (3, 4), 'col': '#ff310011'},
{'level2' : (5, 6), 'col': '#ff312221'}]
>>> l = 'level0', 'level1', 'level2'
>>> for d in list_dicts:
... print(next(filter(None, map(d.get, l))))
...
(1, 2)
(3, 4)
(5, 6)
There's no convenient builtin, but you could implement it easily enough:
def getfirst(d, keys):
for key in keys:
if key in d:
return d[key]
return None
I would use next with a comprehension:
# build list of keys
levels = [ 'level' + str(i) for i in range(3) ]
for d in list_dicts:
level_key = next(k for k in levels if d.get(k))
level = d[level_key]
Should work on all Pythons:
# a list of dicts
list_dicts = [{'level0': (1, 2), 'col': '#ff310021'},
{'level1': (3, 4), 'col': '#ff310011'},
{'level2': (5, 6), 'col': '#ff312221'}]
# Prioritized (ordered) list of keys [level0, level99]
KEYS = ['level{}'.format(i) for i in range(100)]
# loop over the list of dicts dicts, extract the tuple value whose key is
# like level*
for d in list_dicts:
try:
k = next(k for k in KEYS if k in d)
t = d[k]
col = d['col']
do_something(t, col)
except StopIteration:
pass
Just as a novelty item, here is a version that first computes a getter using functional composition.
if 'reduce' not in globals():
from functools import reduce
list_dicts = [ {'level0' : (1, 2), 'col': '#ff310021'},
{'level1' : (3, 4), 'col': '#ff310011'},
{'level2' : (5, 6), 'col': '#ff312221'}]
levels = list(map('level{}'.format, range(3)))
getter = reduce(lambda f, key: lambda dct: dct.get(key, f(dct)), reversed(levels), lambda _: None)
print(list(map(getter, list_dicts)))
# [(1, 2), (3, 4), (5, 6)]
I wanted to learn how to use dictionary comprehension and decided to use one for the previously solved task. I need to assign multiple values to the same key. I was wondering if there's a better way to achieve what I'm trying to do than with the code I've written so far.
graph = {(x1,y1): [(c,d) for a,b,c,d in data if a == x1 and b == y1] for x1 ,y1, x2, y2 in data}
For example I have this:
data = {(1,2,1,5),(1,2,7,2),(1,5,4,7),(4,7,7,5)}
The first two values should create a key and the remaining two should be added as a value of a key.
With the given example I would like to return:
{(1, 2): [(1, 5), (7, 2)], (1, 5): [(4, 7)], (4, 7): [(7, 5)]}
Is there an easier way to do it than iterate through the entire data just to find the matching values?
Using this dict comprehension isn’t an efficient way here. It loops over the same input data repeatedly.
It's more Pythonic to just use a simple for loop, iterating the data only once:
from collections import defaultdict
data = {(1,2,1,5),(1,2,7,2),(1,5,4,7),(4,7,7,5)}
output = defaultdict(list)
for a, b, c, d in data:
output[a, b].append((c, d))
Your code is neat but the time complexity is O(n^2), which can be reduced to O(n).
data = {(1,2,1,5),(1,2,7,2),(1,5,4,7),(4,7,7,5)}
result = dict()
for item in data:
key = (item[0],item[1])
value = result.setdefault(key,[])
value.append((item[2],item[3]))
result[key] = value
print result
In my opinion, using a for loop can make codes more comprehensive
I don't know if it is the best answer but I would do something like that :
m_dict = {}
for val in data:
key = (val[0],val[1])
if key in m_dict:
m_dict[key].append((val[2],val[3]))
else:
m_dict[key] = [(val[2],val[3])]
Or more concisely using setdefault:
m_dict = {}
for val in data:
key = (val[0],val[1])
obj = m_dict.setdefault(key,[])
obj.append((val[2],val[3]))
In this instance, I would use itertools.groupby. For your example:
dict(groupby(data, lambda t: (t[0], t[1])))
This will produce a dict with the keys equal to (1, 2), (1, 5), and (4, 7) and the values consisting of (1, 2, 1, 5), (1, 2, 7, 2)... which should be sufficient for most uses. You can also post-process the grouped list, if need be.
As noted in the comments below, groupby requires sorted data. As such, you will need to sort before grouping and will probably want to cast the iterator to a list:
first_two = lambda tup: (tup[0], tup[1])
groups = groupby(sorted(data, key=first_two), first_two)
target = {k: list(g) for k, g in groups}