维护序列项顺序的同时消除重复项
在序列中,经常会碰到有重复项的情况,有时需要消除重复的项。
解决方案:使用set来构造无重复数据项类型。如:
a = [1, 5, 2, 1, 9, 1, 5, 10]
>>> set(a)
{1, 2, 10, 5, 9}
使用set构造数据后,原始的序列元素失去了当初的相对顺序。同时简单的使用set会有另一个问题:可哈希。我们知道,像list中包含list或dict等复合类型且作为set的构造函数参数进行set对象创建时,会有以下报错:
>>> c = [{'x': 1, 'y': 2}, {'x': 2, 'y': 3}]
>>> set(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
为了解决上述问题,我们可以写一个函数先消除hash的序列的重复项,同时保持原有子项的相对顺序。
def dedupe(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
>>> a = [1, 5, 2, 1, 9, 1, 5, 10]
>>> list(dedupe(a))
[1, 5, 2, 9, 10]
通过构造一个生成器,并按原始顺序遍历items。每次遍历的时候,检查元素是否在集合容器中,若不在,则返回,并添加到set中。若已在set中,则跳过,继续遍历。
该函数仅对序列中所有子项全部可hash时有用。否则集合在进行add操作时会报错。
我们可以改进上述函数的实现,使其可以对不可hash(如dict)的对象进行特殊处理,如下:
def dedupe(items, key=None):
seen = set()
for item in items:
val = item if key is None else key(item)
if val not in seen:
yield item
seen.add(val)
>>> a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
>>> list(dedupe(a, key=lambda d: (d['x'],d['y'])))
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
>>> list(dedupe(a, key=lambda d: d['x']))
[{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
>>>
提供一个类似max, min中的key函数参数,然后在实际调用时使用lambda匿名函数。若key不为None时,实际val的值由匿名函数的返回值提供。