【问题标题】:Compare two lists of tuples比较两个元组列表
【发布时间】:2017-09-01 13:44:21
【问题描述】:
old = [('ver','1121'),('sign','89'),('address','A45'),('type','00')]
new = [('ver','1121'),('sign','89'),('type','01')]

我需要根据元组的第一个元素将new 列表与old 进行比较,并显示new 列表具有的任何元素之间的差异,以便输出应如下所示:

Match     : ver   =   1121
Match     : sign  =   89
Mismatch  : type  =   01 (old : 00)

我可以通过以下列表理解获得所有匹配的元组,但无法超越它。

my_list = [(a,b) for (a,b) in new for (c,d) in old  if ((a==c) and (b==d))]
print( my_list)

请给我建议一种方法。

编辑

对不起,我的问题没有说清楚,我没有再提一件事,列表中的键可以重复,这意味着列表可以是:

old = [('ver','1121'),('sign','89'),('address','A45'),('type','00'),('ver','sorry')]

new = [('ver','1121'),('sign','89'),('type','01'),('ver','sorry)]

更新

感谢@holdenweb,我对他的代码进行了一些更改,这似乎提供了预期的输出,如果有任何缺陷,请提出建议。

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])))

【问题讨论】:

  • address 会发生什么?
  • 如果列表推导无法实现,请建议是否有任何其他方式,例如遍历列表。
  • 因为它不在两个列表中,所以没有@MooingRawr
  • @MooingRawr 比较应该只适用于 new 中可用的项目
  • 输出的顺序重要吗?列表的顺序例如:ver 应该在前,sign 下一个

标签: python list tuples list-comprehension


【解决方案1】:

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')}

【讨论】:

  • 这是一个 OP 可以用来找出不匹配的工具 :)
【解决方案2】:

有时列表理解不是答案。这可能是其中之一。此外,您不会处理old 中存在密钥但new 中不存在密钥的情况 - 我在此处包括该情况,但如果该代码不相关,您可以删除该代码。您可以类似地处理 new 中缺少键的情况,但我没有走那么远。

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))

【讨论】:

  • 我们可以使用enumerate() 遍历这两个列表来处理重复键吗?
  • enumerate 只是按顺序为项目分配数字,所以我不确定它会有什么帮助。但是,如果列表变大,则有许多可能的策略。首先想到的是为每个列表构建一个字典,其键是元组的第一个元素,其值是该元组在列表中的位置。这样可以更快地确定正确的列表元素,而无需对其进行迭代(这是一个 O(n/2) 操作。
【解决方案3】:

这个呢:

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]))

输出:

Mismatch  :type     01       00
Match     :ver      1121     
Match     :sign     89      

如果您需要下单,请尝试使用OrderedDict

from collections import OrderedDict
n,o=OrderedDict(new),OrderedDict(old)

【讨论】:

    【解决方案4】:

    你可以这样做:

    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
    

    这会给你:

    Match :  ('ver', '1121')
    Match :  ('sign', '89')
    Mismatch :  ('address', 'A45')
    Mismatch :  ('type', '00')
    

    但肯定有一种更“pythonic”的方式....

    【讨论】:

      【解决方案5】:

      这是获取所有比较列表的单行代码。根据您的旧列表和新列表的大小,集合理解的工作速度会更快一些,但是我的sorted + itertools.groupby 下面的方法会抵消这种速度(因为sorted 返回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 现在是:

      [('ver', 'same', '1121'),
       ('sign', 'same', '89'),
       ('type', 'changed', '00', '01')]
      

      打印出来:

      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
      

      编辑:以下内容并不完全是 OP 想要的,但无论如何我都会将其留给那些有兴趣查看哪些内容首先保持不变以及哪些内容发生了变化的人(实际上,您可以定义一个自定义语法来按 if 排序)您想查看首先更改的元素,但这取决于您)。

      准备使用itertools.groupby,并考虑到上面显示的OP对打印顺序的请求,我们可以使用collections.OrderedDict对象来有效地创建“键”的“有序集”:

      import collections
      
      proper_order = tuple(collections.OrderedDict.fromkeys([x[0] for x in new]).keys())
      
      proper_order
      Out[43]: ('ver', 'sign', 'type')
      

      现在根据proper_order中的自定义语法对comps进行排序:

      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')]
      

      使用itertools.groupby 打印:

      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
      

      恰好这个输出的顺序与上面 OP 要求的顺序相同,但是对于更大的情况,两种方法之间的顺序差异会变得很明显。

      【讨论】:

      • “订单很重要”是什么意思?你的意思是在“改变”之前需要“相同”?
      • @not_a_robot,如果我在两个列表中都有重复的键,它会起作用吗?比如说,新旧列表多了一个元组('ver','blah')
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      • 2020-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-07
      相关资源
      最近更新 更多