|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
4 # |
|
5 # This software is subject to the provisions of the Zope Public License, |
|
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
|
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
|
10 # FOR A PARTICULAR PURPOSE. |
|
11 # |
|
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
|
15 |
|
16 # import standard library |
|
17 from itertools import filterfalse |
|
18 from random import random, shuffle |
|
19 |
|
20 # import interfaces |
|
21 |
|
22 # import packages |
|
23 |
|
24 |
|
25 def unique(seq, key=None): |
|
26 """Extract unique values from list, preserving order |
|
27 |
|
28 :param iterator seq: input list |
|
29 :param callable key: an identity function which is used to get 'identity' value of each element |
|
30 in the list |
|
31 :return: list; a new list containing only unique elements of the original list in their initial order. |
|
32 Original list is not modified. |
|
33 |
|
34 >>> from pyams_utils.list import unique |
|
35 >>> mylist = [1, 2, 3, 2, 1] |
|
36 >>> unique(mylist) |
|
37 [1, 2, 3] |
|
38 |
|
39 >>> mylist = [3, 2, 2, 1, 4, 2] |
|
40 >>> unique(mylist) |
|
41 [3, 2, 1, 4] |
|
42 |
|
43 You can also set an 'id' function applied on each element: |
|
44 |
|
45 >>> mylist = [1, 2, 3, '2', 4] |
|
46 >>> unique(mylist, key=str) |
|
47 [1, 2, 3, 4] |
|
48 >>> mylist = ['A', 'B', 'b', '2', 4] |
|
49 >>> unique(mylist, key=lambda x: str(x).lower()) |
|
50 ['A', 'B', '2', 4] |
|
51 """ |
|
52 seen = set() |
|
53 seen_add = seen.add |
|
54 result = [] |
|
55 if key is None: |
|
56 for element in filterfalse(seen.__contains__, seq): |
|
57 seen_add(element) |
|
58 result.append(element) |
|
59 else: |
|
60 for element in seq: |
|
61 k = key(element) |
|
62 if k not in seen: |
|
63 seen_add(k) |
|
64 result.append(element) |
|
65 return result |
|
66 |
|
67 |
|
68 def unique_iter(iterable, key=None): |
|
69 """Iterate over iterator values, yielding only unique values |
|
70 |
|
71 :param iterator iterable: input iterator |
|
72 :param callable key: an identity function which is used to get 'identity' value of each element |
|
73 in the list |
|
74 :return: an iterator of unique values |
|
75 |
|
76 >>> from pyams_utils.list import unique_iter |
|
77 >>> mylist = [1, 2, 3, 2, 1] |
|
78 >>> list(unique_iter(mylist)) |
|
79 [1, 2, 3] |
|
80 |
|
81 >>> mylist = [3, 2, 2, 1, 4, 2] |
|
82 >>> list(unique_iter(mylist)) |
|
83 [3, 2, 1, 4] |
|
84 |
|
85 You can also set an 'id' function applied on each element: |
|
86 |
|
87 >>> mylist = [1, 2, 3, '2', 4] |
|
88 >>> list(unique_iter(mylist, key=str)) |
|
89 [1, 2, 3, 4] |
|
90 >>> mylist = ['A', 'B', 'b', '2', 4] |
|
91 >>> list(unique_iter(mylist, key=lambda x: str(x).lower())) |
|
92 ['A', 'B', '2', 4] |
|
93 """ |
|
94 seen = set() |
|
95 seen_add = seen.add |
|
96 if key is None: |
|
97 for element in filterfalse(seen.__contains__, iterable): |
|
98 seen_add(element) |
|
99 yield element |
|
100 else: |
|
101 for element in iterable: |
|
102 k = key(element) |
|
103 if k not in seen: |
|
104 seen_add(k) |
|
105 yield element |
|
106 |
|
107 |
|
108 def random_iter(iterable, limit=1): |
|
109 """Get items randomly from an iterator |
|
110 |
|
111 >>> from pyams_utils.list import random_iter |
|
112 >>> mylist = [1, 2, 3, 2, 1] |
|
113 >>> list(random_iter(mylist, 2)) |
|
114 [..., ...] |
|
115 """ |
|
116 selected = [None] * limit |
|
117 for index, item in enumerate(iterable): |
|
118 if index < limit: |
|
119 selected[index] = item |
|
120 else: |
|
121 selected_index = int(random() * (index+1)) |
|
122 if selected_index < limit: |
|
123 selected[selected_index] = item |
|
124 shuffle(selected) |
|
125 return iter(selected) |