使用Ramdajs将Imperative转换为功能风格范例 [英] Converting Imperative to Functional style paradigm using Ramdajs
问题描述
以下脚本创建一个过滤一些输入数据的对象。
它使用几个嵌套 forEach
以声明方式编码。
我想知道哪个用于使用 ramdajs 或 lodash ,特别是我会有兴趣了解如果使用管道< a>在这种情况下是适当的,否则就是另一种方式。
一个代码示例将会很感激(特别是对于ramdajs)。谢谢。
$ b
var data = {type:stylesheet, stylesheet:{rules:[{type:keyframes,name:bounce,keyframes:[{type:keyframe,values:[from, 20%,53%,80%,to],声明:[{type:声明,属性:animation-timing-function,value立方贝兹(0.215,0.610,0.355,1.000),位置:{start:{line:3,column:5},end:{line:3, :72}}},{type:declaration,property:transform, value:translate3d(0,0,0),position:{start:{line:4,column:5},end:{line:4,列:34}}}],position:{start:{line:2,column:3},end:{line:5,column:4}} },{type:keyframe,values:[40%,43%],声明:[{type:declaration,property函数,值:立方贝塞尔(0.755,0.050,0.855,0.060),位置:{开始:{行:8,列:5},结束:{ line:8,column:72}}},{ type:声明,属性:transform,value:translate3d(0,-30px,0),position:{start:{line:9, :5,end:{line:9,column:40}}}],position:{start:{line:7,column:3}结尾:{line:10,column:4}}},{type:keyframe,values:[70%],declarations:[{type:声明,属性:动画定时功能,值:立方贝塞尔(0.755,0.050,0.855,0.060),位置:{start:{line 列:5},结束:{行:13, column:72}}},{type:declaration,property:transform,value:translate3d(0,-15px,0),position:{start :{line:14,column:5},end:{line:14,column:40}}}],position:{start:{line 12,column:3},end:{line:15,column:4}},{type:keyframe,values:[90%],声明:[{type:declaration,property:transform,value:translate3d(0,-4px,0),position:{start:{line :18,列:5},结束:{ line:18,column:37}}}],position:{start:{line:17,column:3},end:{line column:4}}}],position:{start:{line:1,column:1},end:{line:20,column:2} }},{type:rule,selectors:[.bounce],declarations:[{type:declaration,property:animation-name :bounce,position:{start:{line:23,column:3},end:{line:23,column:25}},{键入:声明,属性:变换原点,值: 中心底,位置:{start:{line:24,column:3},end:{line:24,column:34}}]位置:{start:{line:22,column:1},end:{line:25,column:2}},{type:keyframes ,name:spark,keyframes:[{type:keyframe,values:[0%,50%],声明:[{type:声明,属性:transform,value:translate3d(0,0,0),position:{start:{line:29,column:5}结束:{line:29,column:34}}}],position:{ start:{line:28,column:3},end:{line:30,column:4}},{type:keyframe,values :[100%],声明:[{type:declaration,property:transform,value:translate3d(0,-4px,0),position: {start:{line:32,column:5},end:{line:32,column:37}}}],position:{start line:31,column:3},end:{line:33,column:4}}}],position:{start:{line column:1},end:{line:34 ,column:2}}},{type:rule,selectors:[.spark],declarations:[{type:declaration,property:animation -name,value:spark,position:{start:{line:37,column:3},end:{line:37,column 24}},{type:declaration,property:transform-origin,value:center center,position:{start:{line:38,列:3},end:{line:38,column:34}}}],position:{start:{line:36,column:1}, end:{line:39,column:2}}}],parsingErro rs:[]}}; var result = {}; var kfs = data.stylesheet.rules.filter(function(rule){return rule.type ==='keyframes'}); kfs.forEach(function(kf){result [kf.name] = []; kf.keyframes.forEach(function(kfi){kfi.values.forEach(function(v){var r = {}; var vNew; vNew = v; if(v ==='from'){vNew = 0;} else if(v ==='to'){vNew = 100;} else {vNew = parseFloat(v);} r.offset = vNew; kfi.declarations.forEach(function(d){r [d.property] = d.value;}); result [kf.name] .push(r);});});}); console.log(result);
到目前为止,我能够在ramdajs中实现这个结果:
var rulesLense = R.lensPath(['stylesheet','rules']);
var ruleView = R.view(rulesLense,obj);
var keyframes = R.filter(R.propEq('type','keyframes'));
var groupByKeyframe = R.groupBy(keyframe => {
return R.prop('name',keyframe);
});
var process = R.pipe(
keyframes,
groupByKeyframe
);
var result = process(ruleView);
。要使用镜头修改结构,建议使用 applySpec
和 evolve
,这些对于返回新版本的对象非常有用修改后的值。但是你正在寻找将数据转化为与原始树非常不同的东西,我认为它是AST。在Ramda中, pipe
和 compose
是essentials,它可以通过编写小函数来构造代码。为了处理树,我使用 converge
进行分支, objOf
和 zipObj
创建新的对象。同时 map
和减少
以使用列表。
在本例中,我将使用以下组合策略:
transformAST
^
|
|
getContentOfKeyframes
^ ^
| |
| |
processKeyframe processAnimation
首先,我们创建一个接收 values
和一个声明
的数组,它返回一个数组,它在第一个位置有一个转换值数组,在第二个位置定位一个对象,其中的键是声明属性
的值,并且这些值是其相应的声明值
。
var processKeyframe =(vals,declarations)=> [
//映射每个值
R.map(R.cond([
[R.equals('from'),R.always(0)],
[ R.equals('to'),R.always(100)],
[RT,parseFloat]
]),vals),
//收集所有属性值对并合并一个对象
R.reduce(R.merge,{},
R.map(R.converge(R.objOf,[
R.prop('property'),
.prop('value')
]),声明))
]
现在让我们创建一个处理动画的函数,它接收一个偏移量
的数组和一个带有变换的对象,返回一个带有签名<$ c $的新对象的数组c> {offset:offset,... trasformations} 。
var processAnimation =(offset, transf)=>
R.map(R.pipe(
R.objOf('offset'),
R.merge(transf)),offset)
接下来,通过组合前两个函数来映射每个关键帧:
var getContentOfKeyframes = R.map(R.pipe(
// process keyframes
R.converge(processKeyframe,[
R.prop('values'),
R.prop('声明')
]),
//处理动画
R.converge(processAnimation,[
R.nth(0),
R .nth(1)
])))
最后,我们定义函数,所需的属性从
data
,总结了每个关键帧,最后在最后阶段给出了所需的格式。
var transformAST = R.pipe(
//获取`stylesheet.rules`属性
R.path(['stylesheet','rules']),
//只获得`type`属性为'keyframes`的对象
R.filter(R.propEq('typ e','keyframes')),
//映射`keyframes`中的每个项目
// //到一个对象{name:keyframe.name,content:[contentOfkeyframes]}
R .map((keyframe)=> ({
name:keyframe.name,
content:getContentOfKeyframes(keyframe.keyframes)
})),
//最后使用动画`name`作为键创建一个新对象
//并使用扁平内容作为值
R.converge(R.zipObj,[
R.map(R.prop('name')),
。 map(R.pipe(R.prop('content'),R.flatten))
)))
现在您可以直接处理AST传递
数据
对象的内容。var result = transformAST(data)
所有在一起。
var processKeyframe =(vals,declarations)=> [
R.map(R.cond([
[R.equals('from'),R.always(0)],
[R.equals('to'), R.always(100)],
[RT,parseFloat]
]),vals),
R.reduce(R.merge,{},
R.map R.converge(R.objOf,[
R.prop('property'),
R.prop('value')
)),declarations))
]
var processAnimation =(offset,transf)=>
R.map(R.pipe(
R.objOf('offset'),
R.merge(transf)),offset)
var getContentOfKeyframes = R.map(R.pipe(
R.converge(processKeyframe,[
R.prop('values'),
R.prop('declarations')
)) ,
R.converge(processAnimation,[
R.nth(0),
R.nth(1)
])))
var transformAST = R.pipe(
R.path(['stylesheet','rules']),
R.filter(R.propEq('type','keyframes')),
.map((keyframe)=>({
name:keyframe.name,
content:getContentOfKeyframes(keyframe.keyframes)
})),
R.converge (R.zipObj,[
R.map(R.prop('name')),
R.map(R.pipe(R.prop('content'),R.flatten))
)))
var result = transformAST(data)
The following script create an object filtering some input data. It is coded in a declarative way using several nested
forEach
.I would like to know which API to use in rewritting this code using ramdajs or lodash, specially I would be interested in understand if use of pipe is appropriate in this case otherwise another way.
An example of code would be appreciate (specially for ramdajs). Thanks.
var data = { "type": "stylesheet", "stylesheet": { "rules": [{ "type": "keyframes", "name": "bounce", "keyframes": [{ "type": "keyframe", "values": [ "from", "20%", "53%", "80%", "to" ], "declarations": [{ "type": "declaration", "property": "animation-timing-function", "value": "cubic-bezier(0.215, 0.610, 0.355, 1.000)", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 72 } } }, { "type": "declaration", "property": "transform", "value": "translate3d(0,0,0)", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 34 } } }], "position": { "start": { "line": 2, "column": 3 }, "end": { "line": 5, "column": 4 } } }, { "type": "keyframe", "values": [ "40%", "43%" ], "declarations": [{ "type": "declaration", "property": "animation-timing-function", "value": "cubic-bezier(0.755, 0.050, 0.855, 0.060)", "position": { "start": { "line": 8, "column": 5 }, "end": { "line": 8, "column": 72 } } }, { "type": "declaration", "property": "transform", "value": "translate3d(0, -30px, 0)", "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 9, "column": 40 } } }], "position": { "start": { "line": 7, "column": 3 }, "end": { "line": 10, "column": 4 } } }, { "type": "keyframe", "values": [ "70%" ], "declarations": [{ "type": "declaration", "property": "animation-timing-function", "value": "cubic-bezier(0.755, 0.050, 0.855, 0.060)", "position": { "start": { "line": 13, "column": 5 }, "end": { "line": 13, "column": 72 } } }, { "type": "declaration", "property": "transform", "value": "translate3d(0, -15px, 0)", "position": { "start": { "line": 14, "column": 5 }, "end": { "line": 14, "column": 40 } } }], "position": { "start": { "line": 12, "column": 3 }, "end": { "line": 15, "column": 4 } } }, { "type": "keyframe", "values": [ "90%" ], "declarations": [{ "type": "declaration", "property": "transform", "value": "translate3d(0,-4px,0)", "position": { "start": { "line": 18, "column": 5 }, "end": { "line": 18, "column": 37 } } }], "position": { "start": { "line": 17, "column": 3 }, "end": { "line": 19, "column": 4 } } }], "position": { "start": { "line": 1, "column": 1 }, "end": { "line": 20, "column": 2 } } }, { "type": "rule", "selectors": [ ".bounce" ], "declarations": [{ "type": "declaration", "property": "animation-name", "value": "bounce", "position": { "start": { "line": 23, "column": 3 }, "end": { "line": 23, "column": 25 } } }, { "type": "declaration", "property": "transform-origin", "value": "center bottom", "position": { "start": { "line": 24, "column": 3 }, "end": { "line": 24, "column": 34 } } }], "position": { "start": { "line": 22, "column": 1 }, "end": { "line": 25, "column": 2 } } }, { "type": "keyframes", "name": "spark", "keyframes": [{ "type": "keyframe", "values": [ "0%", "50%" ], "declarations": [{ "type": "declaration", "property": "transform", "value": "translate3d(0,0,0)", "position": { "start": { "line": 29, "column": 5 }, "end": { "line": 29, "column": 34 } } }], "position": { "start": { "line": 28, "column": 3 }, "end": { "line": 30, "column": 4 } } }, { "type": "keyframe", "values": [ "100%" ], "declarations": [{ "type": "declaration", "property": "transform", "value": "translate3d(0,-4px,0)", "position": { "start": { "line": 32, "column": 5 }, "end": { "line": 32, "column": 37 } } }], "position": { "start": { "line": 31, "column": 3 }, "end": { "line": 33, "column": 4 } } }], "position": { "start": { "line": 27, "column": 1 }, "end": { "line": 34, "column": 2 } } }, { "type": "rule", "selectors": [ ".spark" ], "declarations": [{ "type": "declaration", "property": "animation-name", "value": "spark", "position": { "start": { "line": 37, "column": 3 }, "end": { "line": 37, "column": 24 } } }, { "type": "declaration", "property": "transform-origin", "value": "center center", "position": { "start": { "line": 38, "column": 3 }, "end": { "line": 38, "column": 34 } } }], "position": { "start": { "line": 36, "column": 1 }, "end": { "line": 39, "column": 2 } } }], "parsingErrors": [] } }; var result = {}; var kfs = data.stylesheet.rules.filter(function(rule) { return rule.type === 'keyframes' }); kfs.forEach(function(kf) { result[kf.name] = []; kf.keyframes.forEach(function(kfi) { kfi.values.forEach(function(v) { var r = {}; var vNew; vNew = v; if (v === 'from') { vNew = 0; } else if (v === 'to') { vNew = 100; } else { vNew = parseFloat(v); } r.offset = vNew; kfi.declarations.forEach(function(d) { r[d.property] = d.value; }); result[kf.name].push(r); }); }); }); console.log(result);
EDIT:
So far I was able to achieve this result in ramdajs:
var rulesLense = R.lensPath(['stylesheet', 'rules']); var ruleView = R.view(rulesLense, obj); var keyframes = R.filter(R.propEq('type', 'keyframes')); var groupByKeyframe = R.groupBy(keyframe => { return R.prop('name', keyframe); }); var process = R.pipe( keyframes, groupByKeyframe ); var result = process(ruleView);
解决方案Traversing complex structures by using just Ramda is hard but elegant. To modify a structure using lenses,
applySpec
andevolve
is recommendable, these are very useful to return a new version of objects with modified values. But you are looking to transform the data in something very different to the original tree, which I assume is an AST. In Ramda,pipe
andcompose
are essentials, it makes possible to structure the code by composing small functions. For working with trees I useconverge
for branching,objOf
andzipObj
to create new objects. Alsomap
andreduce
to work with lists.I'm going to use the following composition strategy in this example:
transformAST ^ | | getContentOfKeyframes ^ ^ | | | | processKeyframe processAnimation
To start with, let's create a function that receives an array of
values
and an array ofdeclarations
, it returns an array which in the first position has an array of converted values, in the second position an object where the keys are the value of declarationproperty
and the values are its corresponding declarationvalue
.var processKeyframe = (vals, declarations) => [ // map each value R.map(R.cond([ [R.equals('from'), R.always(0)], [R.equals('to'), R.always(100)], [R.T, parseFloat] ]), vals), // collect all property value pairs and merge in one object R.reduce(R.merge, {}, R.map(R.converge(R.objOf, [ R.prop('property'), R.prop('value') ]), declarations)) ]
Now let's create a function to process animations, it receives an array of
offsets
and an object with transformations, returns an array of new objects with the signature{offset: offset, ...trasformations}
.var processAnimation = (offsets, transf) => R.map(R.pipe( R.objOf('offset'), R.merge(transf)), offsets)
Next, map each keyframe by composing the two previous functions
var getContentOfKeyframes = R.map(R.pipe( // process keyframes R.converge(processKeyframe, [ R.prop('values'), R.prop('declarations') ]), // process animations R.converge(processAnimation, [ R.nth(0), R.nth(1) ])))
Finally, we define function that gets the needed properties from
data
, summarizes, each keyframe and finally gives the desired format in the last stage.var transformAST = R.pipe( // get `stylesheet.rules` property R.path(['stylesheet', 'rules']), // get only object whose `type` property is `keyframes` R.filter(R.propEq('type', 'keyframes')), // map each item in `keyframes` collection // to an object {name: keyframe.name, content: [contentOfkeyframes] } R.map((keyframe) => ({ name : keyframe.name, content : getContentOfKeyframes(keyframe.keyframes) })), // finally make a new object using animation `name` as keys // and using a flatten content as values R.converge(R.zipObj, [ R.map(R.prop('name')), R.map(R.pipe(R.prop('content'), R.flatten)) ]))
Now you can process the AST directly passing the
data
object.var result = transformAST(data)
All together.
var processKeyframe = (vals, declarations) => [ R.map(R.cond([ [R.equals('from'), R.always(0)], [R.equals('to'), R.always(100)], [R.T, parseFloat] ]), vals), R.reduce(R.merge, {}, R.map(R.converge(R.objOf, [ R.prop('property'), R.prop('value') ]), declarations)) ] var processAnimation = (offsets, transf) => R.map(R.pipe( R.objOf('offset'), R.merge(transf)), offsets) var getContentOfKeyframes = R.map(R.pipe( R.converge(processKeyframe, [ R.prop('values'), R.prop('declarations') ]), R.converge(processAnimation, [ R.nth(0), R.nth(1) ]))) var transformAST = R.pipe( R.path(['stylesheet', 'rules']), R.filter(R.propEq('type', 'keyframes')), R.map((keyframe) => ({ name : keyframe.name, content : getContentOfKeyframes(keyframe.keyframes) })), R.converge(R.zipObj, [ R.map(R.prop('name')), R.map(R.pipe(R.prop('content'), R.flatten)) ])) var result = transformAST(data)
这篇关于使用Ramdajs将Imperative转换为功能风格范例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!