Skip to content

Commit 31e9675

Browse files
serrasFlavio Corpa
and
Flavio Corpa
authored
feat(collect): generate a lens if possible (#34)
Co-authored-by: Flavio Corpa <[email protected]>
1 parent 38a8be3 commit 31e9675

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,13 @@ Generates a new object whose keys are based on the given optics. Depending on th
189189
collect({ edad: optic('age') }).view({ name: 'Alex', age: 32 }) // { edad: 32 }
190190
```
191191

192+
In addition, if *every* optic is a lens (or can be turned into one), then the result is also a lens. This makes `over` able to change part of a data structure depending on the value of other elements.
193+
194+
```js
195+
collect({ month: optic('birthmonth'), age: optic('age') })
196+
.over(x => { ...x, age: x.month === 'april' ? x.age + 1 : x.age }, person)
197+
```
198+
192199
#### `transform : (s -> a) -> Getter s a`
193200

194201
Applies a transformation to the values targetted up to then. Since the transformation may not be reversible, after composing with `transform` you lose the ability to set or modify the value.

__tests__/operations.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,16 @@ describe('Operations over Optics', () => {
130130
const obj = { one: 1, two: 2 }
131131
expect(view(o, obj)).toBe(3)
132132
})
133+
134+
test('collect + over', () => {
135+
const o = optic(collect({ a: optic('one'), b: optic('two') }))
136+
const obj = { one: 1, two: 2 }
137+
expect(set(o, { a: 2, b: 3 }, obj)).toStrictEqual({ one: 2, two: 3 })
138+
})
139+
140+
test('collect + over', () => {
141+
const o = optic(collect({ a: optic('one'), b: optic('two') }))
142+
const obj = { one: 1, two: 2 }
143+
expect(over(o, ({ a, b }) => ({ a, b: a + b }), obj)).toStrictEqual({ one: 1, two: 3 })
144+
})
133145
})

src/operations.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,12 @@ export const firstOf = (...optics) => {
185185
}
186186
}
187187

188-
export const collect = template =>
189-
getter(obj =>
188+
export const collect = template => {
189+
const t = Object.entries(template)
190+
191+
const computeGetter = () => obj =>
190192
Object.fromEntries(
191-
Object.entries(template).map(([k, o]) => {
193+
t.map(([k, o]) => {
192194
if (o.asGetter) {
193195
return [k, view(o, obj)]
194196
} else if (o.asPartialGetter) {
@@ -199,8 +201,13 @@ export const collect = template =>
199201
throw new OpticComposeError('collect', o.constructor.name, 'non-getter optic')
200202
}
201203
}),
202-
),
203-
)
204+
)
205+
206+
const computeSetter = () => (newVal, obj) =>
207+
t.reduce((acc, [k, o]) => set(o, newVal[k], acc), obj)
208+
209+
return t.every(([, o]) => o.asLens) ? lens(computeGetter(), computeSetter()) : getter(computeGetter())
210+
}
204211

205212
export const transform = getter
206213

0 commit comments

Comments
 (0)