使用 Python 的列表列表的差异

Differences in lists of lists using Python

我有两个列表,如下所示。我知道我可以使用 set(list1)-set(list2) 或 vice-versa 来打印与其他相应列表不同的列表。但是,我不希望打印出完整的列表,我只想要列表中已修改的部分。

例如,list1:

[['Code', 'sID', 'dID', 'cID', 'ssID'], ['ABCD-00', 'ABCD-00-UNK', '358', '1234', '9999'], ['ABCD-01', 'ABCD-00-UNK', 160, '993', '587']]

列表 2:

[['Code', 'sID', 'dID', 'cID', 'ssID', 'AddedColumn'], ['ABCD-00', 'ABCD-00-UNK', '358', '1234', '9999', 'AddedValue1'], ['ABCD-01', 'ABCD-00-UNK', 160, '993', 'ChangedValue', 'AddedValue2']]

如果我设置差异,它会打印出整个列表。当 'Code'、'sID' 相同时,我希望输出仅显示 different/added/taken 之外的列。

每个列表列表的第一个列表是header。所以我想在 'Code'、'sID' 列的值匹配时比较列表。

期望的输出:

Added - ['AddedColumn', 'AddedValue1', 'AddedValue2']
Deleted - []
Changed - ['Code', 'ABCD-01', 'ssID', 'ChangeValue']

像这样或任何更简单的东西也可以。

我试过的代码:

from difflib import SequenceMatcher

matcher = SequenceMatcher()
for a, b in zip(list1, list2):
    matcher.set_seqs(a, b)
    for tag, i1, i2, j1, j2 in matcher.get_opcodes():
        if tag == 'equal': continue
        print('{:>7s} {} {}'.format(tag, a[i1:i2], b[j1:j2]))

它在比较相应的列表时效果很好,即 list1 中的 sub-list1 和 list2 中的 sub-list1。但我希望它在整个列表中进行比较,因为如果缺少特定的 sub-list,它会打印出一切都不一样。 sub-list 我的意思是,例如 list1 中的 ['Code', 'sID', 'dID', 'cID', 'ssID'] 是 sub-list1.

所以 - 正如人们在评论中所说的那样,你真正应该做的是将你正在调用的每组数据读入 "sublists" 到适当的对象中 - 然后他们比较这些对象的属性。

例如,要坚持使用本机类型,如果 "Code" 和 "sID" 构成您的键,则每一行都可以是一个字典,由您的代码和 sid 值的元组作为键。

但是这个问题似乎需要自定义 class - -

鉴于上面的列表之一 - 您几乎可以从以下内容开始:

class MyThing(object):
     def __init__(self, *args):
         for attrname, arg in zip(['Code', 'sID', 'dID', 'cID', 'ssID'], args):
            setattr(self, attrname, arg)

     def __hash__(self):
         # This is not needed for the OrderedDict bwellow, but allows you
         # to use sets with the objects if you want
         return hash(self.Code + self.sID)

from collections import OrderedDict
myobjs = OrderedDict()
for line in list1[1:]:
    obj = MyThing(line)
    id = obj.Code + obj.sId
    if id in myobjs:
        # do your comparisson -logging -printing stuff here
    else:
        myobjs[id] = obj

它实际上可以在没有 class 和对象创建部分的情况下完成 - 只需将 "line" 存储在字典中 - 但是 class 使你能够在字典中做很多事情一种更清洁的方式。复杂的__init__只是一个shorthand不要重复很多self.sId = sId行。

这是我的初步解释。 OP 不太清楚他们对 changed 列表的要求 - 因此他们应该更具体地更新他们的要求。正如 jsbueno 所建议的那样,dict 可能更好——这真的取决于,如果这是它进来的格式,列表会更便宜。

added = []
deleted = []
changed = []
for  sub_l1, sub_l2 in zip(l1, l2): 
    for i in range(min(len(sub_l1), len(sub_l2))): 
        if sub_l1[i] != sub_l2[i]: 
            changed.append(sub_l2[i])
    if len(sub_l2) > len(sub_l1): 
        added.append(sub_l2[len(sub_l1):len(sub_l2)])
    elif len(sub_l1) > len(sub_l2):
        deleted.append(sub_l1[len(sub_l2):len(sub_l1)])

示例输出:

In [66]: added
Out[66]: [['AddedColumn'], ['AddedValue1'], ['AddedValue2']]
In [67]: deleted
Out[67]: []
In [68]: changed
Out[68]: ['ChangedValue'] 

请注意 changed 并没有告诉您更改了哪个值,通常您可能需要一个包含 CSV 子列表和列号的元组。