src/pyams_utils/list.py
changeset 289 c8e21d7dd685
child 292 b338586588ad
equal deleted inserted replaced
-1:000000000000 289:c8e21d7dd685
       
     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)