可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
old = [('ver','1121'),('sign','89'),('address','A45'),('type','00')]
new = [('ver','1121'),('sign','89'),('type','01')]
I need to compare the new
list against old
one based on first element of the tuples, and show the difference between whatever elements new
list has, so that the output should look like:
Match : ver = 1121
Match : sign = 89
Mismatch : type = 01 (old : 00)
I could get all the matching tuples with below list comprehension but could not think beyond it.
my_list = [(a,b) for (a,b) in new for (c,d) in old if ((a==c) and (b==d))]
print( my_list)
Please suggest me a way to do it.
EDIT
I am sorry for not being clear on my question , I did not mention one more thing, the keys in the list can be repetitive, meaning the list can be like:
old = [('ver','1121'),('sign','89'),('address','A45'),('type','00'),('ver','sorry')]
new = [('ver','1121'),('sign','89'),('type','01'),('ver','sorry)]
UPDATE
Thanks to @holdenweb, I've made some changes to his code and this seems to be providing the expected output, please suggest if there are any flaws.
old = [('ver','1121'),('sign','89'),('address','A45'),('type','00'),('ver','works?')]
new = [('ver','1121'),('sign','89'),('type','01'),('ver','This')]
formatter = "{:12}: {:8} = {}".format
newfmter = "{} (old : {})".format
kv_old = []
for i,(kn, vn) in enumerate(new):
vo = [(j,(ko,vo)) for j,(ko, vo) in enumerate(old) if (ko==kn) ]
for idx,(key,val) in vo:
if idx >=i:
kv_old = [key,val]
break;
if kv_old[1]==vn:
print(formatter("Match", kv_old[0], kv_old[1]))
else:
print(formatter("Mismatch", kn, newfmter(vn, kv_old[1])))
回答1:
You can use a set
:
>>> old = [('ver','1121'),('sign','89'),('address','A45'),('type','00')]
>>> new = [('ver','1121'),('sign','89'),('type','01')]
>>> print('no longer there:', set(old) - set(new))
no longer there: {('type', '00'), ('address', 'A45')}
>>> print('newly added:', set(new) - set(old))
newly added: {('type', '01')}
>>> print('still there:', set(old) & set(new))
still there: {('sign', '89'), ('ver', '1121')}
回答2:
Sometimes a list comprehension isn't the answer. This may be one of those times. Also, you don't handle the case where a key is present in old
but not in new
- I include that case here, though you can chop out that code if it isn't relevant. You could similarly handle the case of keys missing from new
, but I didn't go that far.
old = [('ver','1121'),('sign','89'),('address','A45'),('type','00')]
new = [('ver','1121'),('sign','89'),('type','01'),("sneaky", 'not there before')]
formatter = "{:12}: {:8} = {}".format
newfmter = "{} (old : {})".format
for (kn, vn) in new:
if any(ko==kn for (ko, vo) in old):
ko, vo = [(ko, vo) for (ko, vo) in old if ko==kn][0]
if vo==vn:
print(formatter("Match", ko, vo))
else:
print(formatter("Mismatch", kn, newfmter(vn, vo)))
else:
print(formatter("New", kn, vn))
回答3:
you can do something like :
old = [('ver','1121'),('sign','89'),('address','A45'),('type','00')]
new = [('ver','1121'),('sign','89'),('type','01')]
my_list = [(a,b) for (a,b) in new for (c,d) in old if ((a==c) and (b==d))]
for i in old:
if i in my_list:
print "Match : ", i
else:
print "Mismatch : ", i
that will give you :
Match : ('ver', '1121')
Match : ('sign', '89')
Mismatch : ('address', 'A45')
Mismatch : ('type', '00')
But for sure there is a more "pythonic" way....
回答4:
Here's a one-liner to get a list of all comparisons. Depending on how big your old and new lists are, a set comprehension would work a bit more quickly, but that speed would be nullified by my sorted
+ itertools.groupby
approach below (as sorted
returns a list
):
comps = [(key, 'changed', old_val, new_val)
if old_val != new_val else (key, 'same', old_val)
for key, old_val in old for oth_key, new_val in new
if key == oth_key]
comps
is now:
[('ver', 'same', '1121'),
('sign', 'same', '89'),
('type', 'changed', '00', '01')]
Print it out:
for t in comps:
if len(t) == 3:
print('%s: %s, value: %s' % (t[1], t[0], t[2]))
else:
print('%s: %s, value: %s' % (t[1], t[0], ', '.join(t[2:])))
same: ver, value: 1121
same: sign, value: 89
changed: type, value: 00, 01
Edit: The following isn't exactly what OP wants, but I'll leave it anyway for those interested in seeing what stayed the same first, and what changed second (actually, you could define a custom grammar to sort by if you wanted to see the elements that changed first, but that's up to you).
Prepare for using itertools.groupby
, and taking into account OP's request for the printing order shown above, we can use a collections.OrderedDict
object to create effectively an "ordered set" of "keys":
import collections
proper_order = tuple(collections.OrderedDict.fromkeys([x[0] for x in new]).keys())
proper_order
Out[43]: ('ver', 'sign', 'type')
Now sort comps
based on the custom grammar in proper_order
:
comps_sorted = sorted(comps, key=lambda x: proper_order.index(x[0]))
comps_sorted
Out[45]:
[('ver', 'same', '1121'),
('sign', 'same', '89'),
('type', 'changed', '00', '01')]
Use itertools.groupby
to print:
for key, group in itertools.groupby(comps_sorted, key=lambda x: x[1]):
for g in group:
print('%s: %s' % (key, ', '.join(x for x in g if x not in ('same', 'changed'))))
same: ver, 1121
same: sign, 89
changed: type, 00, 01
It just so happens that the order of this output is the same as what OP requested above, but for larger cases, the difference in order between the two approaches will become evident.
回答5:
What about this:
n,o=dict(new),dict(old)
for i in n:
print "{0:10}:{2:8} {3:8} {1}".format(*(("Match","") if o.get(i)==n[i] else ("Mismatch",o.get(i,i)))+ (i,n[i]))
Output:
Mismatch :type 01 00
Match :ver 1121
Match :sign 89
If you need the order, try to use OrderedDict
:
from collections import OrderedDict
n,o=OrderedDict(new),OrderedDict(old)