--- 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
--- 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 = [
--- 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