source: snakeoil/snakeoil/lists.py @ ferringb@gmail.com-20100530120829-a7x3mi6dj7237ene

Revision ferringb@gmail.com-20100530120829-a7x3mi6dj7237ene, 5.3 KB checked in by Brian Harring <ferringb@…>, 2 months ago (diff)

add predicate_split function

Line 
1# Copyright: 2005 Brian Harring <ferringb@gmail.com>
2# License: BSD/GPL2
3
4"""
5sequence related operations
6"""
7
8from itertools import imap
9from snakeoil.iterables import expandable_chain
10
11def unstable_unique(sequence):
12    """
13    lifted from python cookbook, credit: Tim Peters
14    Return a list of the elements in s in arbitrary order, sans duplicates
15    """
16
17    try:
18        n = len(sequence)
19    except TypeError:
20        # if it doesn't support len, assume it's an iterable
21        # and fallback to the slower stable_unique
22        return stable_unique(sequence)
23    # assume all elements are hashable, if so, it's linear
24    try:
25        return list(set(sequence))
26    except TypeError:
27        pass
28
29    # so much for linear.  abuse sort.
30    try:
31        t = sorted(sequence)
32    except TypeError:
33        pass
34    else:
35        assert n > 0
36        last = t[0]
37        lasti = i = 1
38        while i < n:
39            if t[i] != last:
40                t[lasti] = last = t[i]
41                lasti += 1
42            i += 1
43        return t[:lasti]
44
45    # blah.  back to original portage.unique_array
46    u = []
47    for x in sequence:
48        if x not in u:
49            u.append(x)
50    return u
51
52def stable_unique(iterable):
53    """
54    return unique list from iterable, preserving ordering
55    """
56    return list(iter_stable_unique(iterable))
57
58def iter_stable_unique(iterable):
59    """
60    generator yielding unique elements from iterable, preserving ordering
61    """
62    s = set()
63    sadd = s.add
64    sl = []
65    slappend = sl.append
66    iterable = iter(iterable)
67    # the reason for this structuring is purely speed- entering try/except
68    # repeatedly is costly, thus structure it to penalize the unhashables
69    # instead of penalizing the hashables.
70    while True:
71        try:
72            for x in iterable:
73                if x not in s:
74                    yield x
75                    sadd(x)
76        except TypeError:
77            # unhashable item...
78            if x not in sl:
79                yield x
80                slappend(x)
81            continue
82        break
83
84def native_iflatten_instance(l, skip_flattening=(basestring,)):
85    """
86    collapse [[1],2] into [1,2]
87
88    @param skip_flattening: list of classes to not descend through
89    """
90    if isinstance(l, skip_flattening):
91        yield l
92        return
93    iters = expandable_chain(l)
94    try:
95        while True:
96            x = iters.next()
97            if hasattr(x, '__iter__') and not isinstance(x, skip_flattening):
98                iters.appendleft(x)
99            else:
100                yield x
101    except StopIteration:
102        pass
103
104def native_iflatten_func(l, skip_func):
105    """
106    collapse [[1],2] into [1,2]
107
108    @param skip_func: a callable that returns True when iflatten_func should
109        descend no further
110    """
111    if skip_func(l):
112        yield l
113        return
114    iters = expandable_chain(l)
115    try:
116        while True:
117            x = iters.next()
118            if hasattr(x, '__iter__') and not skip_func(x):
119                iters.appendleft(x)
120            else:
121                yield x
122    except StopIteration:
123        pass
124
125
126try:
127    # No name "readdir" in module osutils
128    # pylint: disable-msg=E0611
129    from snakeoil._lists import iflatten_instance, iflatten_func
130    cpy_builtin = True
131except ImportError:
132    cpy_builtin = False
133    cpy_iflatten_instance = cpy_iflatten_func = None
134    iflatten_instance = native_iflatten_instance
135    iflatten_func = native_iflatten_func
136
137
138class ChainedLists(object):
139    """
140    sequences chained together, without collapsing into a list
141    """
142    __slots__ = ("_lists", "__weakref__")
143
144    def __init__(self, *lists):
145        """
146        all args must be sequences
147        """
148        # ensure they're iterable
149        for x in lists:
150            iter(x)
151
152        if isinstance(lists, tuple):
153            lists = list(lists)
154        self._lists = lists
155
156    def __len__(self):
157        return sum(len(l) for l in self._lists)
158
159    def __getitem__(self, idx):
160        if idx < 0:
161            idx += len(self)
162            if idx < 0:
163                raise IndexError
164        for l in self._lists:
165            l2 = len(l)
166            if idx < l2:
167                return l[idx]
168            idx -= l2
169        else:
170            raise IndexError
171
172    def __setitem__(self, idx, val):
173        raise TypeError("not mutable")
174
175    def __delitem__(self, idx):
176        raise TypeError("not mutable")
177
178    def __iter__(self):
179        for l in self._lists:
180            for x in l:
181                yield x
182
183    def __contains__(self, obj):
184        return obj in iter(self)
185
186    def __str__(self):
187        return "[ %s ]" % ", ".join(str(l) for l in self._lists)
188
189    def append(self, item):
190        self._lists.append(item)
191
192    def extend(self, items):
193        self._lists.extend(items)
194
195def predicate_split(func, stream, key=None):
196    true_l, false_l = [], []
197    tappend, fappend = true_l.append, false_l.append
198    # needs to be fast... this this since a simple
199    # lambda x:x # is a bit more of a killer then you would think.
200    if key is not None:
201        for item in stream:
202            if func(key(item)):
203                tappend(item)
204            else:
205                fappend(item)
206    else:
207        for item in stream:
208            if func(item):
209                tappend(item)
210            else:
211                fappend(item)
212    return false_l, true_l
Note: See TracBrowser for help on using the repository browser.