使用 Ramdajs 将命令式样式范式转换为函数式样式范式
Converting Imperative to Functional style paradigm using Ramdajs
以下脚本创建一个对象来过滤一些输入数据。
它使用多个嵌套的 forEach
.
以声明方式编码
我想知道在这种情况下使用 ramdajs or lodash, specially I would be interested in understand if use of pipe 重写此代码时使用哪个 API 是合适的,否则另一种方式。
一个代码示例将不胜感激(特别是对于 ramdajs)。谢谢
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);
编辑:
到目前为止,我能够在 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);
仅使用 Ramda 遍历复杂的结构很难但很优雅。要使用镜头修改结构,建议使用 applySpec
和 evolve
,这些对于 return 具有修改值的新版本对象非常有用。但是您希望以与原始树非常不同的方式转换数据,我认为它是 AST。在 Ramda 中,pipe
和 compose
是必不可少的,它可以通过组合小函数来结构化代码。为了处理树,我使用 converge
进行分支,objOf
和 zipObj
来创建新对象。还有 map
和 reduce
来处理列表。
我将在此示例中使用以下组合策略:
transformAST
^
|
|
getContentOfKeyframes
^ ^
| |
| |
processKeyframe processAnimation
首先,让我们创建一个函数,它接收一个 values
数组和一个 declarations
数组,它 return 是一个数组,在第一个位置有一个数组转换值的第二个位置是一个对象,其中键是声明 property
的值,值是其对应的声明 value
.
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))
]
现在让我们创建一个函数来处理动画,它接收一个 offsets
数组和一个具有转换的对象,return 是一个带有签名 {offset: offset, ...trasformations}
的新对象数组。
var processAnimation = (offsets, transf) =>
R.map(R.pipe(
R.objOf('offset'),
R.merge(transf)), offsets)
接下来,通过组合前面的两个函数来映射每个关键帧
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)
])))
最后,我们定义函数,从data
中获取所需的属性,总结每个关键帧,并最终在最后阶段给出所需的格式。
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))
]))
现在您可以直接传递 data
对象来处理 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)],
[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)
我的版本最终看起来与 Yosbel Marin 的完全不同。
const transform = pipe(
path(['stylesheet', 'rules']),
filter(where({'type': equals('keyframes')})),
groupBy(prop('name')),
map(map(kf => map(kfi => map(v => assoc('offset', cond([
[equals('from'), always(0)],
[equals('to'), always(100)],
[T, parseFloat]
])(v), pipe(
map(lift(objOf)(prop('property'), prop('value'))),
mergeAll
)(kfi.declarations)), kfi.values), kf.keyframes)
)),
map(flatten)
);
我这样做是作为代码移植,根本没有真正尝试理解您的数据。 (我很难这样做,这至少在一定程度上是必要的,但这也是一种有趣的方式。)
前两步应该很清楚了,和前面的回答很相似。我们从 data.stylesheet.rules
中获取数据,然后对其进行过滤以仅包含那些 "type" 属性 为 "keyframes" 的规则。 (我选择在过滤器中使用 where
,因为我发现以下内容比 propEq
更具可读性:filter(where({'type': equals('keyframes')}))
,但它们的工作原理相同。紧随其后的是 groupBy(prop('name'))
,这给我们留下了这样的结构:
{
bounce: [obj1, obj2, ...]
spark: [objA, objB, ...]
}
接下来是转型的核心。我将原始文件中的每个 forEach
调用都转换为 map
调用(显然不能总是这样做。)
这个:
map(v => map(lift(objOf)(prop('property'), prop('value'))), kfi.declarations)
将声明部分变成
[
{"animation-timing-function": "cubic-bezier(0.215, 0.610, 0.355, 1.000)",}
{transform: "translate3d(0,0,0)"},
]
通过将 objOf
函数从处理标量值提升为处理 return 此类值的函数,然后传入两个将接受声明的函数。然后这个新函数接受声明和 returns 一个对象。将它映射到声明列表上会得到一个对象列表。将其放入带有 mergeAll
的 pipe
调用中会将这样的列表变成单个对象。
并且此位用单个表达式替换了 if (v === 'from') { ... } else if ...
代码:
cond([
[equals('from'), always(0)],
[equals('to'), always(100)],
[T, parseFloat]
])(v)
returns 0
、100
或 parseFloat(v)
的结果,视情况而定。
将此与 assoc('offset')
和上一步的结果相结合,我们得到结果中的主要对象,例如:
{
"animation-timing-function": "cubic-bezier(0.215, 0.610, 0.355, 1.000)",
offset: 0,
transform: "translate3d(0,0,0)"
}
唯一剩下要做的就是清理所有这些地图留下的嵌套列表:
{
bounce: [[[obj1, obj2, ...]]]
spark: [[[objA, objB, ...]]]
}
我们通过添加 map(flatten)
.
您可以在 Ramda REPL.
上看到实际效果
我不知道这是否可以合理地完全免分。我猜这充其量是困难的,而且最终可读性会大大降低。这段代码可能会很好地分解一些映射到它们自己的调用中的函数,但我将把它留作 reader!
的练习。
以下脚本创建一个对象来过滤一些输入数据。
它使用多个嵌套的 forEach
.
我想知道在这种情况下使用 ramdajs or lodash, specially I would be interested in understand if use of pipe 重写此代码时使用哪个 API 是合适的,否则另一种方式。
一个代码示例将不胜感激(特别是对于 ramdajs)。谢谢
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);
编辑:
到目前为止,我能够在 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);
仅使用 Ramda 遍历复杂的结构很难但很优雅。要使用镜头修改结构,建议使用 applySpec
和 evolve
,这些对于 return 具有修改值的新版本对象非常有用。但是您希望以与原始树非常不同的方式转换数据,我认为它是 AST。在 Ramda 中,pipe
和 compose
是必不可少的,它可以通过组合小函数来结构化代码。为了处理树,我使用 converge
进行分支,objOf
和 zipObj
来创建新对象。还有 map
和 reduce
来处理列表。
我将在此示例中使用以下组合策略:
transformAST
^
|
|
getContentOfKeyframes
^ ^
| |
| |
processKeyframe processAnimation
首先,让我们创建一个函数,它接收一个 values
数组和一个 declarations
数组,它 return 是一个数组,在第一个位置有一个数组转换值的第二个位置是一个对象,其中键是声明 property
的值,值是其对应的声明 value
.
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))
]
现在让我们创建一个函数来处理动画,它接收一个 offsets
数组和一个具有转换的对象,return 是一个带有签名 {offset: offset, ...trasformations}
的新对象数组。
var processAnimation = (offsets, transf) =>
R.map(R.pipe(
R.objOf('offset'),
R.merge(transf)), offsets)
接下来,通过组合前面的两个函数来映射每个关键帧
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)
])))
最后,我们定义函数,从data
中获取所需的属性,总结每个关键帧,并最终在最后阶段给出所需的格式。
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))
]))
现在您可以直接传递 data
对象来处理 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)],
[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)
我的版本最终看起来与 Yosbel Marin 的完全不同。
const transform = pipe(
path(['stylesheet', 'rules']),
filter(where({'type': equals('keyframes')})),
groupBy(prop('name')),
map(map(kf => map(kfi => map(v => assoc('offset', cond([
[equals('from'), always(0)],
[equals('to'), always(100)],
[T, parseFloat]
])(v), pipe(
map(lift(objOf)(prop('property'), prop('value'))),
mergeAll
)(kfi.declarations)), kfi.values), kf.keyframes)
)),
map(flatten)
);
我这样做是作为代码移植,根本没有真正尝试理解您的数据。 (我很难这样做,这至少在一定程度上是必要的,但这也是一种有趣的方式。)
前两步应该很清楚了,和前面的回答很相似。我们从 data.stylesheet.rules
中获取数据,然后对其进行过滤以仅包含那些 "type" 属性 为 "keyframes" 的规则。 (我选择在过滤器中使用 where
,因为我发现以下内容比 propEq
更具可读性:filter(where({'type': equals('keyframes')}))
,但它们的工作原理相同。紧随其后的是 groupBy(prop('name'))
,这给我们留下了这样的结构:
{
bounce: [obj1, obj2, ...]
spark: [objA, objB, ...]
}
接下来是转型的核心。我将原始文件中的每个 forEach
调用都转换为 map
调用(显然不能总是这样做。)
这个:
map(v => map(lift(objOf)(prop('property'), prop('value'))), kfi.declarations)
将声明部分变成
[
{"animation-timing-function": "cubic-bezier(0.215, 0.610, 0.355, 1.000)",}
{transform: "translate3d(0,0,0)"},
]
通过将 objOf
函数从处理标量值提升为处理 return 此类值的函数,然后传入两个将接受声明的函数。然后这个新函数接受声明和 returns 一个对象。将它映射到声明列表上会得到一个对象列表。将其放入带有 mergeAll
的 pipe
调用中会将这样的列表变成单个对象。
并且此位用单个表达式替换了 if (v === 'from') { ... } else if ...
代码:
cond([
[equals('from'), always(0)],
[equals('to'), always(100)],
[T, parseFloat]
])(v)
returns 0
、100
或 parseFloat(v)
的结果,视情况而定。
将此与 assoc('offset')
和上一步的结果相结合,我们得到结果中的主要对象,例如:
{
"animation-timing-function": "cubic-bezier(0.215, 0.610, 0.355, 1.000)",
offset: 0,
transform: "translate3d(0,0,0)"
}
唯一剩下要做的就是清理所有这些地图留下的嵌套列表:
{
bounce: [[[obj1, obj2, ...]]]
spark: [[[objA, objB, ...]]]
}
我们通过添加 map(flatten)
.
您可以在 Ramda REPL.
上看到实际效果我不知道这是否可以合理地完全免分。我猜这充其量是困难的,而且最终可读性会大大降低。这段代码可能会很好地分解一些映射到它们自己的调用中的函数,但我将把它留作 reader!
的练习。