Skip to content

Commit

Permalink
Changed Query.filter() method to support predicate matching.
Browse files Browse the repository at this point in the history
  • Loading branch information
shawnbrown committed Jul 8, 2018
1 parent 418ba3d commit 91a7656
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ COMING SOON (0.9.2)
* Added to_csv() method to quickly save results as a CSV file.
* Changed reduce() method to accept "initializer_factory" as
an optional argument.
* Changed filter() method to support predicate matching.
* Added True and False as predicates to support "truth value testing" on
arbitrary objects (to match on truthy or falsy).
* Fixed get_reader() bug that prevented encoding-fallback recovery when
Expand Down
23 changes: 17 additions & 6 deletions datatest/_query/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,17 @@ def wrapper(iterable):
return _apply_to_data(wrapper, iterable)


def _filter_data(function, iterable):
def _filter_data(predicate, iterable):
if callable(predicate) and not isinstance(predicate, type):
function = predicate
else:
predicate = get_predicate(predicate)
if hasattr(predicate, '_func'):
function = predicate._func
else:
def function(x):
return predicate == x

def wrapper(iterable):
if isinstance(iterable, BaseElement):
raise TypeError(('filter expects a collection of data elements, '
Expand Down Expand Up @@ -671,12 +681,13 @@ def map(self, function):
"""
return self._add_step('map', function)

def filter(self, function=None):
"""Filter elements, keeping only those values for which
*function* returns True. If *function* is None, this method
keeps all elements for which :py:class:`bool` returns True.
def filter(self, predicate=True):
"""Filter elements, keeping only those values that match the
given *predicate*. When *predicate* is True, this method keeps
all elements for which :py:class:`bool` returns True (see
:ref:`predicate-docs` for details).
"""
return self._add_step('filter', function)
return self._add_step('filter', predicate)

def reduce(self, function, initializer_factory=None):
"""Reduce elements to a single value by applying a *function*
Expand Down
29 changes: 25 additions & 4 deletions tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,16 @@ def test_tuple_handling(self):


class TestFilterData(unittest.TestCase):
def test_list_iter(self):
iterable = Result([-4, -1, 2, 3], list)
def test_return_type(self):
func = lambda x: True
result = _filter_data(func, [1, 2, 3])
self.assertIsInstance(result, Result)
self.assertEqual(result.evaluation_type, list)
self.assertEqual(result.fetch(), [1, 2, 3])

def test_list_iter(self):
function = lambda x: x > 0
result = _filter_data(function, iterable)
result = _filter_data(function, [-4, -1, 2, 3])
self.assertEqual(result.fetch(), [2, 3])

def test_bad_iterable_type(self):
Expand Down Expand Up @@ -284,7 +289,23 @@ def test_dict_iter_of_integers(self):
iseven = lambda x: x % 2 == 0
with self.assertRaises(TypeError):
result = _filter_data(iseven, iterable)
#result.fetch()

def test_predicate_handling(self):
predicate = set([-1, 2, 9])
result = _filter_data(predicate, [-1, -4, 2, 3, 2])
self.assertEqual(result.fetch(), [-1, 2, 2])

predicate = re.compile('^[b]\w\w$')
result = _filter_data(predicate, ['foo', 'bar', 'baz', 'qux'])
self.assertEqual(result.fetch(), ['bar', 'baz'])

predicate = True
result = _filter_data(predicate, [1, -1, 'x', 0, '', tuple()])
self.assertEqual(result.fetch(), [1, -1, 'x'])

predicate = False
result = _filter_data(predicate, [1, -1, 'x', 0, '', tuple()])
self.assertEqual(result.fetch(), [0, '', tuple()])


class TestFlattenData(unittest.TestCase):
Expand Down

0 comments on commit 91a7656

Please sign in to comment.