# HG changeset patch # User Damien Correia # Date 1530546069 -7200 # Node ID f139914e2eeec1453ce20d27c17e26a6fcfbd61e # Parent 0579aa1658560acdd99e2e7d8b925ad3ebde7c5e# Parent c110fd0251ff4e564392c4d32e678aa38971f747 merge default diff -r 0579aa165856 -r f139914e2eee buildout.cfg --- a/buildout.cfg Thu Jun 28 12:01:52 2018 +0200 +++ b/buildout.cfg Mon Jul 02 17:41:09 2018 +0200 @@ -96,4 +96,4 @@ eggs = pyams_utils [test] [versions] -pyams_utils = 0.1.16 +pyams_utils = 0.1.17 diff -r 0579aa165856 -r f139914e2eee setup.py --- a/setup.py Thu Jun 28 12:01:52 2018 +0200 +++ b/setup.py Mon Jul 02 17:41:09 2018 +0200 @@ -25,7 +25,7 @@ README = os.path.join(DOCS, 'README.txt') HISTORY = os.path.join(DOCS, 'HISTORY.txt') -version = '0.1.16' +version = '0.1.17' long_description = open(README).read() + '\n\n' + open(HISTORY).read() tests_require = [ diff -r 0579aa165856 -r f139914e2eee src/pyams_utils/list.py --- a/src/pyams_utils/list.py Thu Jun 28 12:01:52 2018 +0200 +++ b/src/pyams_utils/list.py Mon Jul 02 17:41:09 2018 +0200 @@ -14,17 +14,18 @@ # 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 - :param callable idfun: an identity function which is used to get 'identity' value of each element + :param callable key: an identity function which is used to get 'identity' value of each element in the list :return: list; a new list containing only unique elements of the original list in their initial order. Original list is not modified. @@ -41,17 +42,63 @@ 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): + """Iterate over iterator values, yielding only unique values + + :param iterator iterable: input iterator + :param callable key: an identity function which is used to get 'identity' value of each element + in the list + :return: an iterator of unique values + + >>> from pyams_utils.list import unique_iter + >>> mylist = [1, 2, 3, 2, 1] + >>> list(unique_iter(mylist)) + [1, 2, 3] + + >>> mylist = [3, 2, 2, 1, 4, 2] + >>> list(unique_iter(mylist)) + [3, 2, 1, 4] + + You can also set an 'id' function applied on each element: + + >>> mylist = [1, 2, 3, '2', 4] + >>> list(unique_iter(mylist, key=str)) + [1, 2, 3, 4] + >>> mylist = ['A', 'B', 'b', '2', 4] + >>> list(unique_iter(mylist, key=lambda x: str(x).lower())) + ['A', 'B', '2', 4] + """ + 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