calmm-js / partial.lenses

Partial lenses is a comprehensive, high-performance optics library for JavaScript
MIT License
915 stars 36 forks source link

Using traversals in L.pick #109

Closed elysion closed 7 years ago

elysion commented 7 years ago

I am trying to map a data structure received from one API to a format that would be suitable for another API. L.pick works well when using lenses with single focus but in my case, I would also need to be able to pick properties from list items. Consider the following example:

const data = {
  a: [
    {b: 1, c: 1},
    {b: 2, c: 2},
    {b: 3, c: 3}
  ],
  d: {e: 'a'}
}

L.get(
  L.pick({
    stuff: L.pick({
      b: ['a', L.elems, 'b'] // L.elems does not work here
    }),
    otherStuff: ['d', 'e']
  }), data)

const desiredResult = {
  stuff: [
    {b: 1},
    {b: 2},
    {b: 3},
  ],
  otherStuff: 'a'
}

The example will not run, because L.get throws the following error: partial.lenses: `elems` requires an applicative. Given: { map: [Function: sndU] }

I do realize that L.pick expects lenses to be passed in and thus this use of the API is invalid. Anyhow being able to transform data structures in this manner would be very useful imho.

polytypic commented 7 years ago

In case you only want a unidirectional operation, you can freely compose optics with functions. So, you could say (playground):

L.get(
  L.pick({
    stuff: L.collect(['a', L.elems, L.props('b')]),
    otherStuff: ['d', 'e']
  }),
  data)

Above L.collect(...) results in an ordinary function that will be used as a read-only optic.

The company example uses the above technique in one half of the joinView isomorphism constructor.

BTW, the Gitter channel is also good for questions like this.

elysion commented 7 years ago

Oh, did not realize I could use functions like that or use Gitter. Using functions in the lenses do solve the problems I currently have as I do not need to use the lense in the other direction. Being able to use the traversals with pick in both directions sounds like something that could be useful in other cases.

polytypic commented 7 years ago

I just realized that there is likely a better way to handle this case. What you are actually looking for here is a kind of adapter between two forms of the same data. We can express that using isomorphisms. Here is a playground that does just that. In this case the adapter is lossy, but notice that the adapter works in both directions. I'll add the array iso constructor to the library when I have a moment.