diff --git a/README.md b/README.md index 1f063fd..e450739 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,7 @@ R.hasPath(['b'], child) # True - [ ] identical - [x] 0.1.2 identity - [x] ifElse -- [ ] inc +- [x] inc - [ ] includes - [ ] indexBy - [x] 0.1.2 indexOf @@ -465,10 +465,10 @@ class ObjWithoutLength: R.length(ObjWithoutLength()) # float('nan') ``` -- [ ] lens -- [ ] lensIndex -- [ ] lensPath -- [ ] lensProp +- [x] lens +- [x] lensIndex +- [x] lensPath +- [x] lensProp - [x] 0.7.0 lift - [x] 0.7.0 liftN - [x] 0.1.2 lt @@ -556,7 +556,7 @@ R.omit(['v1', 'v3'], obj) # {'v2': 2} - [x] 0.1.2 once - [x] 0.1.2 or - [ ] otherwise -- [ ] over +- [x] over - [ ] pair - [ ] partial - [ ] partialObject @@ -648,7 +648,7 @@ R.propEq(1, 'v1', {'v1': 1}) # True - [x] 0.1.2 reverse - [ ] scan - [ ] sequence -- [ ] set +- [x] set - [x] 0.1.2 slice ```python @@ -737,7 +737,7 @@ Partially supported 1. String type, supported 1. for others, just use str(x) instead -- [ ] toUpper +- [x] toUpper - [ ] transduce - [ ] transpose - [ ] traverse @@ -757,7 +757,7 @@ Partially supported - [x] 0.3.0 unnest - [ ] until - [ ] unwind -- [ ] update +- [x] update - [x] 0.1.2 useWith - [x] 0.1.2 values @@ -776,7 +776,7 @@ R.values({'a': 1, 'b': 2}) # [1, 2] Use `R.keysIn` to get the keys of an object. -- [ ] view +- [x] view - [ ] when - [x] 0.1.4 where diff --git a/ramda/__init__.py b/ramda/__init__.py index ca9701b..085e071 100644 --- a/ramda/__init__.py +++ b/ramda/__init__.py @@ -50,6 +50,7 @@ from .head import head from .identity import identity from .ifElse import ifElse +from .inc import inc from .indexOf import indexOf from .insert import insert from .intersection import intersection @@ -65,6 +66,10 @@ from .last import last from .lastIndexOf import lastIndexOf from .length import length +from .lens import lens +from .lensIndex import lensIndex +from .lensPath import lensPath +from .lensProp import lensProp from .lift import lift from .liftN import liftN from .lt import lt @@ -84,6 +89,7 @@ from .omit import omit from .once import once from .Or import Or +from .over import over from .partition import partition from .path import path from .pathEq import pathEq @@ -109,6 +115,8 @@ from .repeat import repeat from .replace import replace from .reverse import reverse +# pylint: disable=redefined-builtin +from .set import set from .slice import slice from .sort import sort from .sortBy import sortBy @@ -124,6 +132,7 @@ from .toPairs import toPairs from .toPairsIn import toPairsIn from .toString import toString +from .toUpper import toUpper from .trim import trim from .unary import unary from .union import union @@ -132,9 +141,11 @@ from .uniqBy import uniqBy from .uniqWith import uniqWith from .unnest import unnest +from .update import update from .useWith import useWith from .values import values from .valuesIn import valuesIn +from .view import view from .where import where from .xprod import xprod # pylint: disable=redefined-builtin diff --git a/ramda/inc.py b/ramda/inc.py new file mode 100644 index 0000000..25d14cf --- /dev/null +++ b/ramda/inc.py @@ -0,0 +1,3 @@ +from .add import add + +inc = add(1) diff --git a/ramda/lens.py b/ramda/lens.py new file mode 100644 index 0000000..1b4a66d --- /dev/null +++ b/ramda/lens.py @@ -0,0 +1,13 @@ +from .map import map +from .private._curry2 import _curry2 + + +def inner_lens(getter, setter): + def wrapper1(toFunctorFn): + def wrapper2(target): + return map(lambda focus: setter(focus, target), toFunctorFn(getter(target))) + return wrapper2 + return wrapper1 + + +lens = _curry2(inner_lens) diff --git a/ramda/lensIndex.py b/ramda/lensIndex.py new file mode 100644 index 0000000..37da722 --- /dev/null +++ b/ramda/lensIndex.py @@ -0,0 +1,11 @@ +from .lens import lens +from .nth import nth +from .private._curry1 import _curry1 +from .update import update + + +def inner_lensIndex(n): + return lens(nth(n), update(n)) + + +lensIndex = _curry1(inner_lensIndex) diff --git a/ramda/lensPath.py b/ramda/lensPath.py new file mode 100644 index 0000000..621143f --- /dev/null +++ b/ramda/lensPath.py @@ -0,0 +1,11 @@ +from .assocPath import assocPath +from .lens import lens +from .path import path +from .private._curry1 import _curry1 + + +def inner_lensPath(p): + return lens(path(p), assocPath(p)) + + +lensPath = _curry1(inner_lensPath) diff --git a/ramda/lensProp.py b/ramda/lensProp.py new file mode 100644 index 0000000..c94132b --- /dev/null +++ b/ramda/lensProp.py @@ -0,0 +1,11 @@ +from .assoc import assoc +from .lens import lens +from .private._curry1 import _curry1 +from .prop import prop + + +def inner_lensProp(k): + return lens(prop(k), assoc(k)) + + +lensProp = _curry1(inner_lensProp) diff --git a/ramda/over.py b/ramda/over.py new file mode 100644 index 0000000..96610c1 --- /dev/null +++ b/ramda/over.py @@ -0,0 +1,12 @@ +from .private._curry3 import _curry3 + + +def Identity(x): + return {'value': x, 'map': lambda f: Identity(f(x))} + + +def inner_over(lens, f, x): + return lens(lambda y: Identity(f(y)))(x)['value'] + + +over = _curry3(inner_over) diff --git a/ramda/set.py b/ramda/set.py new file mode 100644 index 0000000..ce726c9 --- /dev/null +++ b/ramda/set.py @@ -0,0 +1,11 @@ +from .always import always +from .over import over +from .private._curry3 import _curry3 + + +def inner_set(lens, v, x): + return over(lens, always(v), x) + + +# pylint: disable=redefined-builtin +set = _curry3(inner_set) diff --git a/ramda/toUpper.py b/ramda/toUpper.py new file mode 100644 index 0000000..69aeb49 --- /dev/null +++ b/ramda/toUpper.py @@ -0,0 +1 @@ +def toUpper(s): return s.upper() diff --git a/ramda/update.py b/ramda/update.py new file mode 100644 index 0000000..e8ff567 --- /dev/null +++ b/ramda/update.py @@ -0,0 +1,10 @@ +from .adjust import adjust +from .always import always +from .private._curry3 import _curry3 + + +def inner_update(idx, x, arr): + return adjust(idx, always(x), arr) + + +update = _curry3(inner_update) diff --git a/ramda/view.py b/ramda/view.py new file mode 100644 index 0000000..ab5cb9d --- /dev/null +++ b/ramda/view.py @@ -0,0 +1,12 @@ +from .private._curry2 import _curry2 + + +def Const(x): + return {'value': x, 'fantasy-land/map': lambda *_: Const(x)} + + +def inner_view(lens, x): + return lens(Const)(x)['value'] + + +view = _curry2(inner_view) diff --git a/test/test_inc.py b/test/test_inc.py new file mode 100644 index 0000000..c2d08e9 --- /dev/null +++ b/test/test_inc.py @@ -0,0 +1,22 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/inc.js +""" + + +class TestInc(unittest.TestCase): + def test_increments_its_argument(self): + self.assertEqual(0, R.inc(-1)) + self.assertEqual(1, R.inc(0)) + self.assertEqual(2, R.inc(1)) + self.assertEqual(13.34, R.inc(12.34)) + self.assertEqual(float('-inf'), R.inc(float('-inf'))) + self.assertEqual(float('inf'), R.inc(float('inf'))) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_lens.py b/test/test_lens.py new file mode 100644 index 0000000..0634c1e --- /dev/null +++ b/test/test_lens.py @@ -0,0 +1,70 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/lens.js +""" + +alice = { + 'name': 'Alice Jones', + 'address': ['22 Walnut St', 'San Francisco', 'CA'], + 'pets': {'dog': 'joker', 'cat': 'batman'} +} + +nameLens = R.lens(R.prop('name'), R.assoc('name')) +addressLens = R.lensProp('address') +headLens = R.lensIndex(0) +dogLens = R.lensPath(['pets', 'dog']) + + +class TestLens(unittest.TestCase): + def test_may_be_applie_to_a_lens_created_by_lensPath(self): + self.assertEqual('joker', R.view(dogLens, alice)) + + def test_may_be_applied_to_a_lens_created_by_lensProp(self): + self.assertEqual('Alice Jones', R.view(nameLens, alice)) + + self.assertEqual({ + 'name': 'ALICE JONES', + 'address': ['22 Walnut St', 'San Francisco', 'CA'], + 'pets': {'dog': 'joker', 'cat': 'batman'} + }, R.over(nameLens, R.toUpper, alice)) + + self.assertEqual({ + 'name': 'Alice Smith', + 'address': ['22 Walnut St', 'San Francisco', 'CA'], + 'pets': {'dog': 'joker', 'cat': 'batman'} + }, R.set(nameLens, 'Alice Smith', alice)) + + def test_may_be_applied_to_a_lens_created_by_lensIndex(self): + self.assertEqual('22 Walnut St', R.view(headLens, alice['address'])) + + self.assertEqual(['22 WALNUT ST', 'San Francisco', 'CA'], R.over(headLens, R.toUpper, alice['address'])) + + self.assertEqual(['52 Crane Ave', 'San Francisco', 'CA'], R.set(headLens, '52 Crane Ave', alice['address'])) + + def test_may_be_applied_to_composed_lenses(self): + streetLens = R.compose(addressLens, headLens) + dogLens = R.compose(R.lensProp('pets'), R.lensProp('dog')) + + self.assertEqual(R.view(dogLens, alice), R.view(R.lensPath(['pets', 'dog']), alice)) + + self.assertEqual('22 Walnut St', R.view(streetLens, alice)) + + self.assertEqual({ + 'name': 'Alice Jones', + 'address': ['22 WALNUT ST', 'San Francisco', 'CA'], + 'pets': {'dog': 'joker', 'cat': 'batman'} + }, R.over(streetLens, R.toUpper, alice)) + + self.assertEqual({ + 'name': 'Alice Jones', + 'address': ['52 Crane Ave', 'San Francisco', 'CA'], + 'pets': {'dog': 'joker', 'cat': 'batman'} + }, R.set(streetLens, '52 Crane Ave', alice)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_lensIndex.py b/test/test_lensIndex.py new file mode 100644 index 0000000..982d774 --- /dev/null +++ b/test/test_lensIndex.py @@ -0,0 +1,44 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/lensIndex.js +""" + +testList = [{'a': 1}, {'b': 2}, {'c': 3}] + + +class TestLensIndex(unittest.TestCase): + def test_focuses_list_element_at_the_specified_index(self): + self.assertEqual({'a': 1}, R.view(R.lensIndex(0), testList)) + + def test_returns_None_if_the_specified_index_does_not_exist(self): + self.assertEqual(None, R.view(R.lensIndex(10), testList)) + + def test_sets_the_list_value_at_the_specified_index(self): + self.assertEqual([0, {'b': 2}, {'c': 3}], R.set(R.lensIndex(0), 0, testList)) + + def test_applies_function_to_the_value_at_the_specified_list_index(self): + self.assertEqual([{'a': 1}, {'b': 2}, ['c']], R.over(R.lensIndex(2), R.keys, testList)) + + def test_can_be_composed(self): + nestedList = [0, [10, 11, 12], 1, 2] + composedLens = R.compose(R.lensIndex(1), R.lensIndex(0)) + self.assertEqual(10, R.view(composedLens, nestedList)) + + def test_set_s_get_s_equals_s(self): + # set s (get s) == s + self.assertEqual(testList, R.set(R.lensIndex(0), R.view(R.lensIndex(0), testList), testList)) + + def test_get_set_s_v_equals_v(self): + # get (set s v) == v + self.assertEqual(0, R.view(R.lensIndex(0), R.set(R.lensIndex(0), 0, testList))) + + def test_get_set_set_s_v1_v2_equals_v2(self): + # get (set (set s v1) v2) == v2 + self.assertEqual(11, R.view(R.lensIndex(0), R.set(R.lensIndex(0), 11, R.set(R.lensIndex(0), 10, testList), testList))) + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_lensPath.py b/test/test_lensPath.py new file mode 100644 index 0000000..8c68be0 --- /dev/null +++ b/test/test_lensPath.py @@ -0,0 +1,99 @@ +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/lensPath.js +""" + +testDict = { + 'a': [{ + 'b': 1 + }, { + 'b': 2 + }], + 'd': 3 +} + + +class A: + def __init__(self, b): + self.b = b + + +class Obj: + def __init__(self, a, d): + self.a = a + self.d = d + + +testObj = Obj([A(1), A(2)], 3) + + +class TestLensPath(unittest.TestCase): + def test_focuses_the_specified_object_property(self): + self.assertEqual(3, R.view(R.lensPath(['d']), testDict)) + self.assertEqual(2, R.view(R.lensPath(['a', 1, 'b']), testDict)) + self.assertEqual(testDict, R.view(R.lensPath([]), testDict)) + + self.assertEqual(3, R.view(R.lensPath(['d']), testObj)) + self.assertEqual(2, R.view(R.lensPath(['a', 1, 'b']), testObj)) + self.assertEqual(testObj, R.view(R.lensPath([]), testObj)) + + def test_sets_the_value_of_the_object_property_specified(self): + self.assertEqual({'a': [{'b': 1}, {'b': 2}], 'd': 0}, R.set(R.lensPath(['d']), 0, testDict)) + self.assertEqual({'a': [{'b': 0}, {'b': 2}], 'd': 3}, R.set(R.lensPath(['a', 0, 'b']), 0, testDict)) + self.assertEqual(0, R.set(R.lensPath([]), 0, testDict)) + + # TODO: support object, refer assoc + + def test_adds_the_property_to_the_object_if_it_does_not_exist(self): + self.assertEqual({'a': [{'b': 1}, {'b': 2}], 'd': 3, 'X': 0}, R.set(R.lensPath(['X']), 0, testDict)) + self.assertEqual({'a': [{'b': 1, 'X': 0}, {'b': 2}], 'd': 3}, R.set(R.lensPath(['a', 0, 'X']), 0, testDict)) + + # TODO: support object, refer assoc + + def test_applies_function_to_the_value_of_the_specified_object_property(self): + self.assertEqual({'a': [{'b': 1}, {'b': 2}], 'd': 4}, R.over(R.lensPath(['d']), R.inc, testDict)) + self.assertEqual({'a': [{'b': 1}, {'b': 3}], 'd': 3}, R.over(R.lensPath(['a', 1, 'b']), R.inc, testDict)) + self.assertEqual([['a', [{'b': 1}, {'b': 2}]], ['d', 3]], R.over(R.lensPath([]), R.toPairs, testDict)) + + # TODO: support object, refer assoc + + def test_applies_function_to_None_and_adds_the_property_if_it_does_not_exists(self): + self.assertEqual({'a': [{'b': 1}, {'b': 2}], 'd': 3, 'X': None}, R.over(R.lensPath(['X']), R.identity, testDict)) + self.assertEqual({'a': [{'b': 1, 'X': None}, {'b': 2}], 'd': 3}, R.over(R.lensPath(['a', 0, 'X']), R.identity, testDict)) + + # TODO: support object, refer assoc + + def test_can_be_composed(self): + composedLens = R.compose(R.lensPath(['a']), R.lensPath([1, 'b'])) + self.assertEqual(2, R.view(composedLens, testDict)) + self.assertEqual(2, R.view(composedLens, testObj)) + + def test_set_s_get_s_equals_s(self): + # set s (get s) == s + self.assertEqual(testDict, R.set(R.lensPath(['d']), R.view(R.lensPath(['d']), testDict), testDict)) + self.assertEqual(testDict, R.set(R.lensPath(['a', 0, 'b']), R.view(R.lensPath(['a', 0, 'b']), testDict), testDict)) + + # TODO: support object, refer assoc + + def test_get_set_s_v_equals_v(self): + # get (set s v) == v + self.assertEqual(0, R.view(R.lensPath(['d']), R.set(R.lensPath(['d']), 0, testDict))) + self.assertEqual(0, R.view(R.lensPath(['a', 0, 'b']), R.set(R.lensPath(['a', 0, 'b']), 0, testDict))) + + # TODO: support object, refer assoc + + def test_get_set_set_s_v1_v2_equals_v2(self): + # get (set (set s v1) v2) == v2 + p = ['d'] + q = ['a', 0, 'b'] + self.assertEqual(11, R.view(R.lensPath(p), R.set(R.lensPath(p), 11, R.set(R.lensPath(p), 10, testDict)))) + self.assertEqual(11, R.view(R.lensPath(q), R.set(R.lensPath(q), 11, R.set(R.lensPath(q), 10, testDict)))) + + # TODO: support object, refer assoc + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_lensProp.py b/test/test_lensProp.py new file mode 100644 index 0000000..39c0c1f --- /dev/null +++ b/test/test_lensProp.py @@ -0,0 +1,80 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/lensProp.js +""" + +testDict = { + 'a': 1, + 'b': 2, + 'c': 3 +} + + +class Obj: + def __init__(self, a, b, c): + self.a = a + self.b = b + self.c = c + + +testObj = Obj(1, 2, 3) + + +class TestLensProp(unittest.TestCase): + def test_focuses_object_the_specified_object_property(self): + self.assertEqual(1, R.view(R.lensProp('a'), testDict)) + self.assertEqual(1, R.view(R.lensProp('a'), testObj)) + + def test_returns_None_if_the_specified_property_does_not_exist(self): + self.assertEqual(None, R.view(R.lensProp('X'), testDict)) + self.assertEqual(None, R.view(R.lensProp('X'), testObj)) + + def test_sets_the_value_of_the_object_property_specified(self): + self.assertEqual({'a': 0, 'b': 2, 'c': 3}, R.set(R.lensProp('a'), 0, testDict)) + # TODO: support object, refer assoc + + def test_adds_the_property_to_the_object_if_it_does_not_exist(self): + self.assertEqual({'a': 1, 'b': 2, 'c': 3, 'd': 4}, R.set(R.lensProp('d'), 4, testDict)) + # TODO: support object, refer assoc + + def test_applies_function_to_the_value_of_the_specified_object_property(self): + self.assertEqual({'a': 2, 'b': 2, 'c': 3}, R.over(R.lensProp('a'), R.inc, testDict)) + # TODO: support object, refer assoc + + def test_applies_function_to_the_value_of_the_specified_object_property(self): + self.assertEqual({'a': 2, 'b': 2, 'c': 3}, R.over(R.lensProp('a'), R.inc, testDict)) + # TODO: support object, refer assoc + + def test_applies_function_to_None_and_adds_the_property_if_it_does_not_exist(self): + self.assertEqual({'a': 1, 'b': 2, 'c': 3, 'X': None}, R.over(R.lensProp('X'), R.identity, testDict)) + # TODO: support object, refer assoc + + def test_can_be_composed(self): + nestedObj = { + 'a': { + 'b': 1 + }, + 'c': 2 + } + composedLens = R.compose(R.lensProp('a'), R.lensProp('b')) + self.assertEqual(1, R.view(composedLens, nestedObj)) + + def test_set_s_get_s_equals_s(self): + # set s (get s) == s + self.assertEqual(testDict, R.set(R.lensProp('a'), R.view(R.lensProp('a'), testDict), testDict)) + + def test_get_set_s_v_equals_s_v(self): + # get (set s v) == v + self.assertEqual(1, R.view(R.lensProp('a'), R.set(R.lensProp('a'), 1, testDict))) + + def test_get_set_set_s_v1_v2_equals_v2(self): + # get (set (set s v1) v2) == v2 + self.assertEqual(11, R.view(R.lensProp('a'), R.set(R.lensProp('a'), 11, R.set(R.lensProp('a'), 10, testDict)))) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_toUpper.py b/test/test_toUpper.py new file mode 100644 index 0000000..70ec84e --- /dev/null +++ b/test/test_toUpper.py @@ -0,0 +1,17 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/toUpper.js +""" + + +class TestToUpper(unittest.TestCase): + def test_returns_the_upper_case_equivalent_of_the_input_string(self): + self.assertEqual('ABC', R.toUpper('abc')) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_update.py b/test/test_update.py new file mode 100644 index 0000000..f55298e --- /dev/null +++ b/test/test_update.py @@ -0,0 +1,39 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/update.js +""" + + +class TestUpdate(unittest.TestCase): + def test_updates_the_value_at_the_given_index_of_the_supplied_array(self): + self.assertEqual([0, 1, 4, 3], R.update(2, 4, [0, 1, 2, 3])) + + def test_offsets_negative_indexes_from_the_end_of_the_array(self): + self.assertEqual([0, 4, 2, 3], R.update(-3, 4, [0, 1, 2, 3])) + + def test_returns_the_original_array_if_the_supplied_index_is_out_of_bounds(self): + arr = [0, 1, 2, 3] + self.assertEqual(arr, R.update(4, 4, arr)) + self.assertEqual(arr, R.update(-5, 4, arr)) + + def test_does_not_mutate_the_original_array(self): + arr = [0, 1, 2, 3] + self.assertEqual([0, 1, 4, 3], R.update(2, 4, arr)) + self.assertEqual([0, 1, 2, 3], arr) + + def test_curries_the_arguments(self): + self.assertEqual([0, 1, 4, 3], R.update(2)(4)([0, 1, 2, 3])) + + def test_accpets_an_array_like_object(self): + def args(*a): + return list(a) + + self.assertEqual([0, 1, 4, 3], R.update(2, 4, args(0, 1, 2, 3))) + + +if __name__ == '__main__': + unittest.main()