Added function to get unique members from iterator
authorThierry Florac <thierry.florac@onf.fr>
Fri, 29 Jun 2018 18:15:51 +0200
changeset 210 4f9a1501bba6
parent 209 03c69e5c959b
child 211 48a13f7bddbe
Added function to get unique members from iterator
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