Filtering¶
CherryPicker
objects navigate your data by the following rules:
- If it implements the
collections.abc.Mapping
interface, treat it like adict
; - Otherwise if it implements the
collections.abc.Iterable
interface, treat it like alist
; - Otherwise it is treated as a leaf node (i.e. an end point).
You apply filters to your data by providing predicates in parentheses after you have navigated to the data you want, for example:
>>> picker = CherryPicker(data)
>>> picker(name='Alice')
<CherryPickerIterable(list, len=1)>
…applied the predicate name='Alice'
to the root data node. There were
twelve items in the data that matched this filter. To see the actual data that
was extracted, use the cherrypicker.CherryPicker.get()
method:
>>> picker(name='Alice').get()
[{'name': 'Alice', 'age': 20}]
Filters behave slightly differently depending on what type of data you have at the point you apply them:
- If the data is
dict
-like, each predicate will be applied to the value obtained by the key matching the predicate parameter. In the example above, the value for the keyname
will be checked, and if it is'Alice'
, the filter has passed and the object will be returned. If the filter fails, the default item (which defaults to a leaf containingNone
) is returned instead. - If the data is
list
-like, the filter will be applied to each child. A newlist
-like node will be returned containing only the matching items.
Combining predicates¶
Multiple predicates can be applied in a single filter. The how parameter
determines the logic used to combine them. If how is 'all'
(which is the
default), all predicates must match. If how is 'any'
, only one predicate
needs to match for the filter to pass.
Types of predicate¶
The value supplied for each predicate term determines the kind of test that is performed:
- If the predicate is a string, one of the following checks will be done:
- If allow_wildcards=True and the string contains a wildcard character as
defined by
fnmatch.fnmatchcase()
, then a wildcard match is performed. - If case_sensitive=False, a case-insensitive string comparison will be made.
- If regex=True then the string will be compiled into a regular expression.
A
re.fullmatch()
test will be performed. If case_sensitive is also False, the regex test will be case-insensitive. - Otherwise, only an exact match is accepted.
- If allow_wildcards=True and the string contains a wildcard character as
defined by
- If the predicate is a compiled regular expression pattern, a
re.fullmatch()
test will be performed. - If the predicate is a callable function or lambda, the function will be
applied to the value being tested. This function should take in a single
parameter (the value) and return something that evaluates to
True
orFalse
.
API¶
-
CherryPickerTraversable.
filter
(how='all', allow_wildcards=True, case_sensitive=True, regex=False, opts=None, **predicates) → cherrypicker.traversable.CherryPickerTraversable[source] Return a filtered view of the child nodes. This method is usually accessed via
CherryPicker.__call__()
For an object with a mappable interface, this will return the object itself if it matches the predicates according to the rules specified.
For an object with an iterable but not a mappable interface, a collection of child objects matching the predicates according to the rules specified will be returned.
This method is not implemented for leaf nodes and will cause an error to be raised.
Example: Find any items with a name of
Alice
:>>> picker(name='Alice')
Find any items with a name of
Alice
and an age of 20:>>> picker(name='Alice', age=20)
Find any items with a name of
Alice
or an age of 20:>>> picker(name='Alice', age=20, how='any')
Find any items with a name of
Alice
and an age of 20 or more:>>> picker(name='Alice', age=lambda a: a >= 20)
Find any items with a name beginning with
Al
:>>> picker(name='Al*')
Find any items with a name beginning with
Al
oral
:>>> picker(name='Al*', case_sensitive=False)
Find any items with a name of
Al*
:>>> picker(name='Al*', allow_wildcards=False)
Find any items with a name matching a particular pattern (these two lines are equivalent):
>>> picker(name=r'^(?:Alice|Bob)$', regex=True, case_sensitive=False) >>> picker(name=re.compile(r'^(?:Alice|Bob)$', re.I))
Parameters: - how (str.) – The rule to be applied to predicate matching. May be one of (‘all’, ‘any’).
- allow_wildcards (bool, default = True.) – If True, special characters
(
*
,?
,[]
) in any string predicate values will be treated as wildcards according tofnmatch.fnmatchcase()
. - case_sensitive (bool, default = True.) – If True, any comparisons to strings or uncompiled regular expressions will be case sensitive.
- regex (bool, default = False.) – If True, any string comparisons will be reinterpreted as
regular expressions. If
case_sensitive
is False, they will be case-insensitive patterns. For more complex regex options, omit this parameter and provide pre-compiled regular expression patterns in your predicates instead. All regular expressions will be compared to string values using a full match. - predicates (str, regular expression or Callable.) – Keyword arguments where the keys are the object keys used to get the comparison value, and the values are either a value to compare, a regular expression to perform a full match against, or a callable function that takes a single value as input and returns something that evaluates to True if the value passes the predicate, or False if it does not.
Returns: If this is a mappable object, the object itself if it passes the predicates. If not and this is an iterable object, a collection of children that pass the predicates.
Return type: