diff --git a/README.md b/README.md index 5014aea..6f8bc47 100644 --- a/README.md +++ b/README.md @@ -457,7 +457,7 @@ R.subtract(date(1,2,3), date(1,2,3)) # float('nan) - [ ] takeLast - [ ] takeLastWhile - [x] takeWhile -- [ ] tap +- [x] tap - [ ] test - [ ] thunkify - [ ] times @@ -493,7 +493,19 @@ Partially supported - [ ] unwind - [ ] update - [x] useWith -- [ ] values +- [x] values + +```python +# works for both dict and object +class Obj: + def __init__(self, v1, v2): + self.v1 = v1 + self.v2 = v2 +obj = Obj(1, 2) +R.values(obj) # [1, 2] +R.values({'a': 1, 'b': 2}) # [1, 2] +``` + - [ ] valuesIn - [ ] view - [ ] when diff --git a/ramda/__init__.py b/ramda/__init__.py index 3af7a9f..1e26a0d 100644 --- a/ramda/__init__.py +++ b/ramda/__init__.py @@ -80,6 +80,7 @@ from .tail import tail from .take import take from .takeWhile import takeWhile +from .tap import tap from .toString import toString from .union import union from .unionWith import unionWith @@ -87,3 +88,4 @@ from .uniqBy import uniqBy from .uniqWith import uniqWith from .useWith import useWith +from .values import values diff --git a/ramda/private/_xtap.py b/ramda/private/_xtap.py new file mode 100644 index 0000000..28c5966 --- /dev/null +++ b/ramda/private/_xtap.py @@ -0,0 +1,15 @@ +from ._helper import getAttribute +from ._xfBase import XfBase + + +class XTap(XfBase): + def __init__(self, f, xf): + self.xf = xf + self.f = f + + def step(self, result, _input): + self.f(_input) + return getAttribute(self.xf, '@@transducer/step')(result, _input) + + +def _xtap(f): return lambda xf: XTap(f, xf) diff --git a/ramda/tap.py b/ramda/tap.py new file mode 100644 index 0000000..edb7352 --- /dev/null +++ b/ramda/tap.py @@ -0,0 +1,11 @@ +from .private._curry2 import _curry2 +from .private._dispatchable import _dispatchable +from .private._xtap import _xtap + + +def inner_tap(fn, x): + fn(x) + return x + + +tap = _curry2(_dispatchable([], _xtap, inner_tap)) diff --git a/ramda/values.py b/ramda/values.py new file mode 100644 index 0000000..cb9d919 --- /dev/null +++ b/ramda/values.py @@ -0,0 +1,18 @@ +from .keys import keys +from .private._curry1 import _curry1 +from .private._helper import getAttribute + + +def inner_values(obj): + props = keys(obj) + length = len(props) + vals = [] + idx = 0 + while idx < length: + val = getAttribute(obj, props[idx]) + vals.append(val) + idx += 1 + return vals + + +values = _curry1(inner_values) diff --git a/test/test_tap.py b/test/test_tap.py new file mode 100644 index 0000000..d90869d --- /dev/null +++ b/test/test_tap.py @@ -0,0 +1,55 @@ + +import unittest + +import ramda as R +from ramda.private._curry2 import _curry2 +from ramda.private._isFunction import _isFunction + +from .helpers.listXf import listXf + +""" +https://github.com/ramda/ramda/blob/master/test/tap.js +""" + +appendToList = _curry2(lambda lst, x: lst.append(x)) + + +class TestTap(unittest.TestCase): + def test_returns_a_function_that_always_returns_its_argument(self): + f = R.tap(R.identity) + self.assertTrue(_isFunction(f)) + self.assertEqual(100, f(100)) + self.assertEqual(None, f(None)) + + def test_may_take_a_function_as_the_first_argument_that_executes_with_taps_argument(self): + sideEffect = 0 + self.assertEqual(0, sideEffect) + + def fn(x): + nonlocal sideEffect + sideEffect = 'string ' + str(x) + rv = R.tap(fn, 200) + self.assertEqual(200, rv) + self.assertEqual('string 200', sideEffect) + + def test_can_act_as_a_transducer(self): + sideEffect = [] + numbers = [1, 2, 3, 4, 5] + + xf = R.compose(R.map(R.identity), R.tap(appendToList(sideEffect))) + + self.assertEqual(numbers, R.into([], xf, numbers)) + self.assertEqual(numbers, sideEffect) + + def test_dispatches_to_transformer_objects(self): + sideEffect = [] + appendToSideEffect = appendToList(sideEffect) + + res = R.tap(appendToSideEffect, listXf) + print(res) + self.assertEqual(appendToSideEffect, res.f) + self.assertEqual(listXf, res.xf) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_values.py b/test/test_values.py new file mode 100644 index 0000000..28f9756 --- /dev/null +++ b/test/test_values.py @@ -0,0 +1,56 @@ + +import unittest + +import ramda as R + +""" +https://github.com/ramda/ramda/blob/master/test/values.js +""" + +obj = { + 'a': 100, + 'b': [1, 2, 3], + 'c': { + 'x': 200, + 'y': 300 + }, + 'd': 'D', + 'e': None +} + + +class C: + def __init__(self, a, b): + self.a = 100 + self.b = 200 + + +cobj = C(100, 200) + + +class TestValues(unittest.TestCase): + def test_returns_an_array_of_the_given_object_values(self): + vs = R.values(obj) + ts = [100, [1, 2, 3], {'x': 200, 'y': 300}, 'D', None] + self.assertEqual(len(vs), len(ts)) + self.assertEqual(vs[0], ts[0]) + self.assertEqual(vs[1], ts[1]) + self.assertEqual(vs[2], ts[2]) + self.assertEqual(vs[3], ts[3]) + self.assertEqual(vs[4], ts[4]) + + def test_works_on_objects(self): + self.assertEqual([100, 200], R.values(cobj)) + + def test_returns_an_empty_object_for_primitives(self): + self.assertEqual([], R.values(None)) + self.assertEqual([], R.values(55)) + self.assertEqual([], R.values('foo')) + self.assertEqual([], R.values(True)) + self.assertEqual([], R.values(False)) + self.assertEqual([], R.values([])) + self.assertEqual([], R.values({})) + + +if __name__ == '__main__': + unittest.main()