# HG changeset patch # User Thierry Florac # Date 1530288951 -7200 # Node ID 4f9a1501bba66b6054ad588d0344d2601a15e4ef # Parent 03c69e5c959b29abf9bcdb32002661d80eb116c2 Added function to get unique members from iterator diff -r 03c69e5c959b -r 4f9a1501bba6 src/pyams_utils/list.py --- a/src/pyams_utils/list.py Thu Jun 28 08:35:02 2018 +0200 +++ b/src/pyams_utils/list.py Fri Jun 29 18:15:51 2018 +0200 @@ -14,13 +14,14 @@ # import standard library +from itertools import filterfalse # import interfaces # import packages -def unique(seq, idfun=None): +def unique(seq, key=None): """Extract unique values from list, preserving order :param iterator seq: input list @@ -41,17 +42,41 @@ You can also set an 'id' function applied on each element: >>> mylist = [1, 2, 3, '2', 4] - >>> unique(mylist, idfun=str) + >>> unique(mylist, key=str) [1, 2, 3, 4] + >>> mylist = ['A', 'B', 'b', '2', 4] + >>> unique(mylist, key=lambda x: str(x).lower()) + ['A', 'B', '2', 4] """ - if idfun is None: - def idfun(x): return x - seen = {} + seen = set() + seen_add = seen.add result = [] - for item in seq: - marker = idfun(item) - if marker in seen: - continue - seen[marker] = 1 - result.append(item) + if key is None: + for element in filterfalse(seen.__contains__, seq): + seen_add(element) + result.append(element) + else: + for element in seq: + k = key(element) + if k not in seen: + seen_add(k) + result.append(element) return result + + +def unique_iter(iterable, key=None): + """List unique elements, preserving order. Remember all elements ever seen.""" + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element