diff -r 000000000000 -r c8e21d7dd685 src/pyams_utils/list.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_utils/list.py Wed Dec 05 12:45:56 2018 +0100 @@ -0,0 +1,125 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + + +# import standard library +from itertools import filterfalse +from random import random, shuffle + +# import interfaces + +# import packages + + +def unique(seq, key=None): + """Extract unique values from list, preserving order + + :param iterator seq: input list + :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. + + >>> from pyams_utils.list import unique + >>> mylist = [1, 2, 3, 2, 1] + >>> unique(mylist) + [1, 2, 3] + + >>> mylist = [3, 2, 2, 1, 4, 2] + >>> unique(mylist) + [3, 2, 1, 4] + + You can also set an 'id' function applied on each element: + + >>> mylist = [1, 2, 3, '2', 4] + >>> 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] + """ + seen = set() + seen_add = seen.add + result = [] + 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 + + +def random_iter(iterable, limit=1): + """Get items randomly from an iterator + + >>> from pyams_utils.list import random_iter + >>> mylist = [1, 2, 3, 2, 1] + >>> list(random_iter(mylist, 2)) + [..., ...] + """ + selected = [None] * limit + for index, item in enumerate(iterable): + if index < limit: + selected[index] = item + else: + selected_index = int(random() * (index+1)) + if selected_index < limit: + selected[selected_index] = item + shuffle(selected) + return iter(selected)