merge default doc-dc
authorDamien Correia
Mon, 02 Jul 2018 17:41:09 +0200 (2018-07-02)
branchdoc-dc
changeset 274 f139914e2eee
parent 273 0579aa165856 (current diff)
parent 212 c110fd0251ff (diff)
child 275 59452a6f51a8
merge default
--- 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