Suppose I have the following types
interface State {
parents: Parent[]
}
interface Parent {
uniqueName: string
children: Child[]
// other props
}
interface Child {
uniqueName: string
parentName: string
// other props
}
declare const upsert: (c:Child) => Parent[] => Parent[]
I want to write the last function, upsert. Is there a canonical way of doing this functionally, where given a child (might be a new child or a modified child, the function will look through each parent, and update a matching child (match by uniqueName) or insert the input child if no matching child is found?
The update part is simple optics:
const update = child =>
parentTraversal
.filter(matchesNameOf(child.parent))
.composeLens(_children)
.composeTraversal(childTraversal)
.filter(matchesNameOf(child))
.set(child)(state.parents)
The insert part is a combination:
const insert = child =>
parentTraversal
.filter(matchesNameOf(child.parent))
.composeLens(_children)
.modify(cs => snoc(cs, child))(state.parents)
If I know ahead of time whether I'm updating or inserting, then I could just do
if(isNew) insert(child)(state)
else update(child)(state)
But is there a completely functional way to combine these, especially if you do not know ahead of time whether it's an update or insert?
Edit At the request of a commenter, I'll specify:
declare const _children: Lens<Parent,Child[]>
declare const parentTraversal: Traversal<Parent>
declare const childTraversal: Traversal<Child>
declare const matchesNameOf: <A extends {name:string}>(a:A) => (b:A) => boolean
and then filter, composeTraversal, composeLens, set, and modify are the standard optics functions belonging to lenses and traversals
parentTraversal,childTraversal,matchesNameOf,_children,composeLens,composeTraversal,set,modify, etc? Please provide a minimal workable example.fp-ts. Half of what you're asking about are built in functions for that library!