title: Functional "upsert" on nested array in nested array of objects: use optics somehow?
body: <p>Suppose I have the following types</p>
  <pre class="lang-js prettyprint-override"><code>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) =&gt; Parent[] =&gt; Parent[]
  </code></pre>
  <p>I want to write the last function, <code>upsert</code>. 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 <code>uniqueName</code>) or insert the input child if no matching child is found?</p>
  <p>The <code>update</code> part is simple optics:</p>
  <pre class="lang-js prettyprint-override"><code>const update = child =&gt; 
    parentTraversal
    .filter(matchesNameOf(child.parent))
    .composeLens(_children)
    .composeTraversal(childTraversal)
    .filter(matchesNameOf(child))
    .set(child)(state.parents)
  </code></pre>
  <p>The insert part is a combination:</p>
  <pre class="lang-js prettyprint-override"><code>const insert = child =&gt; 
    parentTraversal
    .filter(matchesNameOf(child.parent))
    .composeLens(_children)
    .modify(cs =&gt; snoc(cs, child))(state.parents)
  </code></pre>
  <p>If I know ahead of time whether I'm updating or inserting, then I could just do</p>
  <pre class="lang-js prettyprint-override"><code>if(isNew) insert(child)(state)
  else update(child)(state)
  </code></pre>
  <p>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?</p>
  <p><strong>Edit</strong> At the request of a commenter, I'll specify:</p>
  <pre class="lang-js prettyprint-override"><code>declare const _children: Lens&lt;Parent,Child[]&gt;
  declare const parentTraversal: Traversal&lt;Parent&gt;
  declare const childTraversal: Traversal&lt;Child&gt;
  declare const matchesNameOf: &lt;A extends {name:string}&gt;(a:A) =&gt; (b:A) =&gt; boolean
  </code></pre>
  <p>and then <code>filter</code>, <code>composeTraversal</code>, <code>composeLens</code>, <code>set</code>, and <code>modify</code> are the standard optics functions belonging to lenses and traversals</p>