Tengo algunos datos en el formulario:
const data = { list: [1, 2, 3], newItem: 5 } Quiero hacer una función que agregue el valor de newItem a la list lo que da como resultado esta nueva versión de data :
{ list: [1,2,3,5], newItem: 5, }(En última instancia, eliminaría newItem después de que se haya movido a la lista, pero estoy tratando de simplificar el problema para esta pregunta).
Estoy tratando de hacerlo usando estilo sin puntos y Ramda.js, como una experiencia de aprendizaje.
Aquí es donde estoy hasta ahora:
const addItem = R.assoc('list', R.pipe( R.prop('list'), R.append(R.prop('newItem')) ) ) La idea es generar una función que acepte data , pero en este ejemplo, la llamada a R.append también necesita una referencia a datos, estoy tratando de evitar mencionar data explícitamente para mantener el estilo Pointfree.
¿Es posible hacerlo sin mencionar data ?
Si entiendo correctamente, desea pasar de {x:3, y:[1,2]} a [1,2,3] . Aquí hay una manera:
const fn = compose(apply(append), props(['x', 'y'])) fn({x:3, y:[1,2]}); //=> [1,2,3]Como muestra la discusión sobre la respuesta de customcommander, hay dos posibles interpretaciones diferentes.
Si solo desea recibir [1, 2, 3, 5] , puede hacerlo como lo hace customcommander, o de la forma en que yo elegiría:
const fn1 = lift (append) (prop ('newItem'), prop ('list')) Pero si quisiera algo como {list: [1, 2, 3, 5], newItem: 5} , entonces podría usar lo anterior dentro de applySpec y combinarlo con una combinación, como esta:
const fn2 = chain (mergeLeft, applySpec ({list: fn1}))Aquí hay un fragmento:
const fn1 = lift (append) (prop ('newItem'), prop ('list')) const fn2 = chain (mergeLeft, applySpec ({list: fn1})) const data = {list: [1, 2, 3], newItem: 5} console .log (fn1 (data)) //=> [1, 2, 3, 5] console .log (fn2 (data)) //=> {list: [1, 2, 3, 5], newItem: 5} .as-console-wrapper {max-height: 100% !important; top: 0} <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script> <script> const {lift, append, prop, chain, mergeLeft, applySpec} = R </script> Este segundo es un poco difícil de manejar, una vez que en línea fn1 . Repite la list de propiedades en dos lugares, y eso siempre me molesta. Pero no tengo una buena solución en este momento.
Varias veces quise una combinación de R.evolve y R.applySpec , que funciona en el exterior como evolve , lo que le permite especificar solo las propiedades que deben cambiar, pero cuyas funciones de transformación se dan en todo el objeto de entrada, y no solo la propiedad correspondiente.
Con algo así, esto podría verse como
const f3 = evolveSpec ({ list: ({list, newItem}) => [...list, newItem] })o usando lo anterior:
const f3 = evolveSpec ({ list: lift (append) (prop ('newItem'), prop ('list')) })Creo que este podría ser un candidato útil para su inclusión en Ramda.
const addItem = R.chain ( R.assoc('list') ) ( R.converge(R.append, [R.prop('newItem'), R.prop('list')]) ); const data = { list: [1, 2, 3], newItem: 5 }; console.log(addItem(data)); <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>Y he aquí por qué:
Primero, podemos echar un vistazo a cómo se supone que debe verse el addItem actual cuando no está libre de puntos:
const addItem = x => R.assoc('list') ( R.pipe( R.prop('list'), R.append(R.prop('newItem')(x)) )(x) )(x); console.log(addItem({ list: [1, 2, 3], newItem: 5 })); <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>Toma algunos datos y los usa en tres lugares. Podemos refactorizar un poco:
const f = R.assoc('list'); const g = x => R.pipe( R.prop('list'), R.append(R.prop('newItem')(x)) )(x) const addItem = x => f(g(x))(x); console.log(addItem({ list: [1, 2, 3], newItem: 5 })); <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script> La parte x => f(g(x))(x) puede no ser obvia de inmediato, pero mirando la lista de combinadores comunes en JavaScript , se puede identificar como S_ :
| Nombre | # | Haskell | Ramda | Santuario | Firma |
|---|---|---|---|---|---|
| cadena | S_³ | (=<<)² | cadena² | cadena² | (a → b → c) → (b → a) → b → c |
Por lo tanto x => f(g(x))(x) se puede simplificar sin puntos a R.chain(f)(g) .
Esto deja la g que todavía toma un argumento y lo usa en dos lugares. El objetivo final es extraer dos propiedades de un objeto y pasarlas a R.append() , esto se puede expresar más fácilmente (y sin puntos) con R.converge() como:
const g = R.converge(R.append, [R.prop('newItem'), R.prop('list')]); Sustituyendo la f y g de nuevo da
const addItem = R.chain ( R.assoc('list') ) ( R.converge(R.append, [R.prop('newItem'), R.prop('list')]) );