我有一个CSS规则对象,该规则可以具有以下任一属性或不具有以下属性:
{ 'font-style': '…',
'font-variant': '…',
'font-weight': '…',
'text-decoration': '…',
'vertical-align': '…' }
下一步是建立一个应用于输入的css字符串,例如:
style({'text-decoration': 'underline'}, 'foo');
//=> '<span style="text-decoration:underline">foo</span>'
但是,如果rules
对象不包含上述五个css规则中的任何一个,则按原样返回输入:
style({}, 'foo'); //=> 'foo'
如您所见,这不是火箭科学,但必须注意不要应用空的CSS字符串或包含我们不需要的额外内容。
我确实使用ramda.js提出了一个解决方案,直到我决定更深入地研究Monads为止。
我对使用Monadic原理可以删除的大量代码印象深刻。
const {curry} = require('ramda');
const {Maybe} = require('monet');
const css = (attrs, key) =>
attrs[key] ?
Maybe.of(`${key}:${attrs[key]};`) :
Maybe.of('');
const style = curry((va, td, fw, fv, fs, input) =>
va || td || fw || fv || fs ?
`<span style="${va}${td}${fw}${fv}${fs}">${input}</span>` : input);
module.exports = curry((attrs, input) =>
Maybe.of(input)
.ap(css('font-style', attrs)
.ap(css('font-variant', attrs)
.ap(css('font-weight', attrs)
.ap(css('text-decoration', attrs)
.ap(css('vertical-align', attrs)
.map(style))))))
.some());
对此我感到满意,但我不禁想到所有这些嵌套的ap
都是某种变相的回调。也许还有我不知道的更好的方法?
问题:是否有更好的方法来组合多个Maybe
单子?
答案 0 :(得分:0)
您真的使事情复杂化了:
const keys = ['font-style', 'font-variant', 'font-weight', 'text-decoration', 'vertical-align'];
const css = attrs => keys
.map(it => attrs[it] && `${it}: ${attrs[it]}`)
.filter(it => it)
.join(", ");
const style = (attrs, input) =>
css(attrs) ? `<span style="${css(attrs)}">${input}</span>` : input;
答案 1 :(得分:0)
如前所述,我认为我没有尽我所能利用Maybe
类型。
我终于确定了以下解决方案:
rules
对象chain
),我决定该对象是否是我可以使用的东西Nothing
,则按原样返回输入,否则我将应用计算出的CSS字符串const styles = (rules, input) =>
Maybe
.of(rules)
.map(pick(['font-style', 'font-variant', 'font-weight', 'text-decoration', 'vertical-align']))
.chain(ifElse(isEmpty, Maybe.none, Maybe.some))
.map(toPairs)
.map(reduce((str, arr) => str + arr.join(':') + ';', ''))
.fold(input)(css => `<span style="${css}">${input}</span>`);
styles({}, 'foo');
//=> 'foo'
styles({'text-decoration':'underline'}, 'foo');
//=> '<span style="text-decoration:underline;">foo</span>'
答案 2 :(得分:0)
这就是我要做的。
const style = (attrs, text) => {
const props = Object.entries(attrs);
if (props.length === 0) return text;
const css = props.map(([key, val]) => key + ":" + val);
return `<span style="${css.join(";")}">${text}</span>`;
};
const example1 = {};
const example2 = { "text-decoration": "underline" };
const example3 = { "font-weight": "bold", "font-style": "italic" };
console.log(style(example1, "foo")); // foo
console.log(style(example2, "foo")); // <span style="text-decoration:underline">foo</span>
console.log(style(example3, "foo")); // <span style="font-weight:bold;font-style:italic;">foo</span>
请注意,尽管这看起来像是命令式代码,但实际上它纯粹是功能性的。可以按以下方式将其音译为Haskell。
import Data.List (intercalate)
import Data.Map.Strict (fromList, toList)
style (attrs, text) =
let props = toList attrs in
if length props == 0 then text else
let css = map (\(key, val) -> key ++ ":" ++ val) props in
"<span style=\"" ++ intercalate ";" css ++ "\">" ++ text ++ "</span>"
example1 = fromList []
example2 = fromList [("text-decoration", "underline")]
example3 = fromList [("font-weight", "bold"), ("font-style", "italic")]
main = do
putStrLn $ style (example1, "foo") -- foo
putStrLn $ style (example2, "foo") -- <span style="text-decoration:underline">foo</span>
putStrLn $ style (example3, "foo") -- <span style="font-weight:bold;font-style:italic;">foo</span>
请注意,不建议将刺耳的单子和合成物插入每个功能程序。
功能编程不仅仅是单子和组合。从本质上讲,所有方面都涉及无副作用地将输入转换为输出。 Monad和组合只是函数式编程提供的一些工具。包装盒中还有更多工具。您只需要找到合适的工具。