使用 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 子列表和列号的元组。
我有两个列表,如下所示。我知道我可以使用 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 子列表和列号的元组。