Skip to content

Commit

Permalink
Merge pull request #176 from zydmayday/lens
Browse files Browse the repository at this point in the history
add method lens and related methods
  • Loading branch information
zydmayday authored Aug 20, 2022
2 parents 78cbf6c + 3d9309f commit f86a7bc
Show file tree
Hide file tree
Showing 19 changed files with 487 additions and 10 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -737,7 +737,7 @@ Partially supported
1. String type, supported
1. for others, just use str(x) instead

- [ ] toUpper
- [x] toUpper
- [ ] transduce
- [ ] transpose
- [ ] traverse
Expand All @@ -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

Expand All @@ -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

Expand Down
11 changes: 11 additions & 0 deletions ramda/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions ramda/inc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .add import add

inc = add(1)
13 changes: 13 additions & 0 deletions ramda/lens.py
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions ramda/lensIndex.py
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions ramda/lensPath.py
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions ramda/lensProp.py
Original file line number Diff line number Diff line change
@@ -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)
12 changes: 12 additions & 0 deletions ramda/over.py
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions ramda/set.py
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions ramda/toUpper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
def toUpper(s): return s.upper()
10 changes: 10 additions & 0 deletions ramda/update.py
Original file line number Diff line number Diff line change
@@ -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)
12 changes: 12 additions & 0 deletions ramda/view.py
Original file line number Diff line number Diff line change
@@ -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)
22 changes: 22 additions & 0 deletions test/test_inc.py
Original file line number Diff line number Diff line change
@@ -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()
70 changes: 70 additions & 0 deletions test/test_lens.py
Original file line number Diff line number Diff line change
@@ -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()
44 changes: 44 additions & 0 deletions test/test_lensIndex.py
Original file line number Diff line number Diff line change
@@ -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()
Loading

0 comments on commit f86a7bc

Please sign in to comment.