Skip to content

Commit

Permalink
add pick
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti committed Jan 16, 2023
1 parent 4c611c3 commit 0af4155
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 60 deletions.
5 changes: 5 additions & 0 deletions .changeset/rotten-rivers-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fp-ts/optic": patch
---

add pick
18 changes: 0 additions & 18 deletions dtslint/ts4.7/at.ts

This file was deleted.

42 changes: 38 additions & 4 deletions dtslint/ts4.7/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import * as Optic from "@fp-ts/optic"

interface Sstruct {
readonly a: string
readonly b: number
readonly a1: string
readonly a2: number
readonly a3: {
readonly b1: boolean
readonly b2: Date
}
}

type Stuple = readonly [string, number]
Expand All @@ -16,15 +20,45 @@ type SindexSignature = Record<string, number>
//

// $ExpectType Lens<Sstruct, string>
Optic.id<Sstruct>().at('a')
Optic.id<Sstruct>().at('a1')
// $ExpectType Lens<Sstruct, string>
Optic.id<Sstruct>().compose(Optic.at('a'))
Optic.id<Sstruct>().compose(Optic.at('a1'))

// $ExpectType Lens<Stuple, string>
Optic.id<Stuple>().at('0')
// $ExpectType Lens<Stuple, string>
Optic.id<Stuple>().compose(Optic.at('0'))

declare const isoSA: Optic.Iso<Sstruct, Sstruct['a3']>
declare const lensSA: Optic.Lens<Sstruct, Sstruct['a3']>
declare const prismSA: Optic.Prism<Sstruct, Sstruct['a3']>
declare const optionalSA: Optic.Optional<Sstruct, Sstruct['a3']>

// $ExpectType Lens<Sstruct, boolean>
isoSA.at('b1')
// $ExpectType Lens<Sstruct, boolean>
lensSA.at('b1')
// $ExpectType Optional<Sstruct, boolean>
prismSA.at('b1')
// $ExpectType Optional<Sstruct, boolean>
optionalSA.at('b1')

//
// pick
//

// $ExpectType Lens<Sstruct, { readonly a1: string; readonly a2: number; }>
Optic.id<Sstruct>().pick('a1', 'a2')

// $ExpectType Lens<Sstruct, { readonly b1: boolean; readonly b2: Date; }>
isoSA.pick('b1', 'b2')
// $ExpectType Lens<Sstruct, { readonly b1: boolean; readonly b2: Date; }>
lensSA.pick('b1', 'b2')
// $ExpectType Optional<Sstruct, { readonly b1: boolean; readonly b2: Date; }>
prismSA.pick('b1', 'b2')
// $ExpectType Optional<Sstruct, { readonly b1: boolean; readonly b2: Date; }>
optionalSA.pick('b1', 'b2')

//
// index
//
Expand Down
16 changes: 0 additions & 16 deletions src/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,3 @@ export const zoom: {
}
return out
}

/**
* An optic that accesses a group of keys of a struct.
*
* @since 1.0.0
*/
export const pick = <S, Keys extends readonly [keyof S, ...Array<keyof S>]>(
...keys: Keys
): Lens<S, { readonly [K in Keys[number]]: S[K] }> =>
Optic.lens((s) => {
const out: any = {}
for (const k of keys) {
out[k] = s[k]
}
return out
}, (a) => (s) => ({ ...s, ...a }))
51 changes: 47 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ export interface Optic<
key: Key
): Optional<S, A[Key]>

/**
* An optic that accesses a group of keys of a struct.
*
* @since 1.0.0
*/
pick<S, A, Keys extends readonly [keyof A, ...Array<keyof A>]>(
this: Lens<S, A>,
...keys: Keys
): Lens<S, { readonly [K in Keys[number]]: A[K] }>
pick<S, A, Keys extends readonly [keyof A, ...Array<keyof A>]>(
this: Optional<S, A>,
...keys: Keys
): Optional<S, { readonly [K in Keys[number]]: A[K] }>

/**
* An optic that accesses the case specified by a predicate.
*
Expand Down Expand Up @@ -143,10 +157,14 @@ class Builder<
prismComposition(that)(this as any)
}

at(key: string) {
at(key: PropertyKey) {
return this.compose(at<any, any>(key))
}

pick(...keys: readonly [PropertyKey, ...Array<PropertyKey>]) {
return this.compose(pick<any, any>(...keys))
}

filter(predicate: Predicate<any>) {
return this.compose(filter(predicate))
}
Expand Down Expand Up @@ -362,6 +380,31 @@ export const at = <S, Key extends keyof S & (string | symbol)>(key: Key): Lens<S
return { ...s, [key]: b }
})

// TODO: replace with @fp-ts/data/Struct
const Struct = {
pick: <S, Keys extends readonly [keyof S, ...Array<keyof S>]>(
...keys: Keys
) =>
(s: S): { [K in Keys[number]]: S[K] } => {
const out: any = {}
for (const k of keys) {
out[k] = s[k]
}
return out
}
}

/**
* An optic that accesses a group of keys of a struct.
*
* @category constructors
* @since 1.0.0
*/
export const pick = <S, Keys extends readonly [keyof S, ...Array<keyof S>]>(
...keys: Keys
): Lens<S, { readonly [K in Keys[number]]: S[K] }> =>
lens(Struct.pick(...keys), (a) => (s) => ({ ...s, ...a }))

/**
* @since 1.0.0
*/
Expand Down Expand Up @@ -567,7 +610,7 @@ export interface IndexSignature<A> {
}

// TODO: replace with @fp-ts/data/ReadonlyRecord
const IS = {
const IndexSignature = {
get: (key: PropertyKey) =>
<A>(is: IndexSignature<A>): Option<A> =>
Object.prototype.hasOwnProperty.call(is, key) ? O.some(is[key]) : O.none,
Expand All @@ -593,7 +636,7 @@ export const key = <A>(key: PropertyKey): Optional<IndexSignature<A>, A> =>
(s) =>
pipe(
s,
IS.get(key),
IndexSignature.get(key),
O.match(
() => E.left(new Error(`hasKey(${String(key)})`)),
E.right
Expand All @@ -602,7 +645,7 @@ export const key = <A>(key: PropertyKey): Optional<IndexSignature<A>, A> =>
(a) =>
(s) =>
pipe(
IS.replaceOption(key, a)(s),
IndexSignature.replaceOption(key, a)(s),
O.match(
() => E.left(new Error(`hasKey(${String(key)})`)),
E.right
Expand Down
18 changes: 0 additions & 18 deletions test/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,4 @@ describe("experimental", () => {
O.some("mike")
)
})

it("pick", () => {
type S = {
readonly a: string
readonly b: number
readonly c: boolean
}

const _pick = Optic.id<S>()
.compose(ExperimentalOptic.pick("a", "b"))

expect(pipe({ a: "a", b: 1, c: true }, Optic.get(_pick))).toEqual({ a: "a", b: 1 })
expect(pipe({ a: "a1", b: 1, c: true }, Optic.replace(_pick)({ a: "a2", b: 2 }))).toEqual({
a: "a2",
b: 2,
c: true
})
})
})
17 changes: 17 additions & 0 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,23 @@ describe("index", () => {
expect(pipe({ b: 2 }, Optic.replace(_keya)(3))).toEqual({ b: 2 })
})

it("pick", () => {
type S = {
readonly a: string
readonly b: number
readonly c: boolean
}

const _ab = Optic.id<S>().pick("a", "b")

expect(pipe({ a: "a", b: 1, c: true }, Optic.get(_ab))).toEqual({ a: "a", b: 1 })
expect(pipe({ a: "a1", b: 1, c: true }, Optic.replace(_ab)({ a: "a2", b: 2 }))).toEqual({
a: "a2",
b: 2,
c: true
})
})

it("head", () => {
const _head = Optic.id<ReadonlyArray<number>>()
.compose(Optic.head())
Expand Down

0 comments on commit 0af4155

Please sign in to comment.