From e67e203a0e85bce59f455dd612180f27491a1504 Mon Sep 17 00:00:00 2001 From: zyd Date: Fri, 27 May 2022 16:01:47 +0900 Subject: [PATCH] add method where --- README.md | 41 ++++++++++++++- ramda/__init__.py | 1 + ramda/where.py | 13 +++++ test/test_where.py | 123 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 ramda/where.py create mode 100644 test/test_where.py diff --git a/README.md b/README.md index fce36f1..12b3c91 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,29 @@ a = A() R.keys(a) # ['a', 'b'] ``` +```python +# keys include super class attributes +class A: + def __init__(self, a): + self.a = a + +class B(A): + def __init__(self, a, b): + super().__init__(a) + self.b = b + +class C(A): + def __init__(self, c): + self.c = c + +a = A(1) +b = B(2, 3) +c = C(4) +R.keys(a) # ['a'] +R.keys(b) # ['a', 'b'] +R.keys(c) # ['c'], because c does not call super().__init__() +``` + - [ ] keysIn - [ ] last - [x] 0.1.2 lastIndexOf @@ -523,7 +546,23 @@ R.values({'a': 1, 'b': 2}) # [1, 2] - [ ] valuesIn - [ ] view - [ ] when -- [ ] where +- [x] where + +spec(first param) is prefer to be a dict. + +method `where` supports both dict and object as second param. + +```python +class Obj: + def __init__(self, x, y): + self.x = x + self.y = y + +spec = {'x': R.equals(1)} +R.where(spec, {'x': 1, 'y': 2}) # True +R.where(spec, Obj(1, 2)) # True +``` + - [ ] whereAny - [ ] whereEq - [ ] without diff --git a/ramda/__init__.py b/ramda/__init__.py index 43537d1..431f37e 100644 --- a/ramda/__init__.py +++ b/ramda/__init__.py @@ -96,6 +96,7 @@ from .uniqWith import uniqWith from .useWith import useWith from .values import values +from .where import where from .xprod import xprod # pylint: disable=redefined-builtin from .zip import zip diff --git a/ramda/where.py b/ramda/where.py new file mode 100644 index 0000000..e7ef5f4 --- /dev/null +++ b/ramda/where.py @@ -0,0 +1,13 @@ +from .private._curry2 import _curry2 +from .private._has import _has +from .private._helper import getAttribute + + +def inner_where(spec, testObj): + for prop in spec: + if _has(spec, prop) and not spec[prop](getAttribute(testObj, prop)): + return False + return True + + +where = _curry2(inner_where) diff --git a/test/test_where.py b/test/test_where.py new file mode 100644 index 0000000..22970c0 --- /dev/null +++ b/test/test_where.py @@ -0,0 +1,123 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/where.js +""" + + +class TestWhereForDict(unittest.TestCase): + def test_returns_true_if_the_test_dict_satisfies_the_spec(self): + spec = {'x': R.equals(1), 'y': R.equals(2)} + test1 = {'x': 0, 'y': 200} + test2 = {'x': 0, 'y': 10} + test3 = {'x': 1, 'y': 101} + test4 = {'x': 1, 'y': 2} + self.assertEqual(False, R.where(spec, test1)) + self.assertEqual(False, R.where(spec, test2)) + self.assertEqual(False, R.where(spec, test3)) + self.assertEqual(True, R.where(spec, test4)) + + def test_does_not_need_the_spec_and_the_test_object_to_have_the_same_interface(self): + spec = {'x': R.equals(100)} + test1 = {'x': 20, 'y': 100, 'z': 100} + test2 = {'w': 1, 'x': 100, 'y': 100, 'z': 100} + + self.assertEqual(False, R.where(spec, test1)) + self.assertEqual(True, R.where(spec, test2)) + + def test_matches_specs_that_have_None_properties(self): + spec = {'x': R.equals(None)} + test1 = {} + test2 = {'x': None} + test3 = {'x': 1} + + self.assertEqual(True, R.where(spec, test1)) + self.assertEqual(True, R.where(spec, test2)) + self.assertEqual(False, R.where(spec, test3)) + +class TestWhereForObject(unittest.TestCase): + def test_returns_true_if_the_test_dict_satisfies_the_spec(self): + class Test: + def __init__(self, x, y): + self.x = x + self.y = y + spec = {'x': R.equals(1), 'y': R.equals(2)} + test1 = Test(0, 200) + test2 = Test(0, 10) + test3 = Test(1, 101) + test4 = Test(1, 2) + self.assertEqual(False, R.where(spec, test1)) + self.assertEqual(False, R.where(spec, test2)) + self.assertEqual(False, R.where(spec, test3)) + self.assertEqual(True, R.where(spec, test4)) + + def test_does_not_need_the_spec_and_the_test_object_to_have_the_same_interface(self): + class Test1: + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + class Test2: + def __init__(self, w, x, y, z): + self.w = w + self.x = x + self.y = y + self.z = z + + spec = {'x': R.equals(100)} + test1 = Test1(20, 100, 100) + test2 = Test2(1, 100, 100, 100) + + self.assertEqual(False, R.where(spec, test1)) + self.assertEqual(True, R.where(spec, test2)) + + def test_matches_specs_that_have_None_properties(self): + class Test1: + def __init__(self): + pass + + class Test2: + def __init__(self, x): + self.x = x + + spec = {'x': R.equals(None)} + test1 = Test1() + test2 = Test2(None) + test3 = Test2(1) + + self.assertEqual(True, R.where(spec, test1)) + self.assertEqual(True, R.where(spec, test2)) + self.assertEqual(False, R.where(spec, test3)) + + def test_is_true_for_an_empty_spec(self): + class Test: + def __init__(self, a): + self.a = a + + self.assertEqual(True, R.where({}, Test(1))) + + def test_matches_super_class_property_if_super_init_called(self): + class Parent: + def __init__(self): + self.x = 1 + + class Child(Parent): + def __init__(self): + super().__init__() + self.y = 2 + + spec1 = {'x': R.equals(1)} + spec2 = {'y': R.equals(2)} + + self.assertEqual(True, R.where(spec1, Child())) + self.assertEqual(True, R.where(spec1, Parent())) + self.assertEqual(True, R.where(spec2, Child())) + self.assertEqual(False, R.where(spec2, Parent())) + + +if __name__ == '__main__': + unittest.main()