我是否使用了错误的 Ramda 函数,R.reduce 似乎没有工作

Am I using the wrong Ramda function, R.reduce doesn't appear to be working

给定一个初始消息字符串(实际上就像一个格式字符串),并包含随后要填充的占位符。

例如我们的初始消息是:

"GREETINGS-{year}-{mm-month}-{dd-day} - HELLO WORLD"

其中有 3 个占位符,{year}{mm-month }, {dd-day}

我想将其翻译成:

"GREETINGS-2016-06-23 - HELLO WORLD"

假设我们有一个对象数组如下:

[{
  "Key": "{year}",
  "Value": "2016"
}, {
  "Key": "{mm-month}",
  "Value": "06"
}, {
  "Key": "{dd-day}",
  "Value": "23"
}]

每个对象都有一个Key对应消息中的占位符字符串,以及用来替换占位符的Value。

我正在使用 R.reduce 函数,因为我们有一个值集合(具有 Key Value 属性的对象列表、字符串替换转换器函数 (tr) 和初始累加器是原始占位符字符串.

变换函数tr定义为:

let tr = (k, v) => { return { Key: k, Value: v } }

执行文本替换的rp函数定义为:

let rp = (pair, msg) => { return R.replace(pair.Key, pair.Value, msg); }

所以期望的效果是迭代列表,用字段值替换字段名称,在每次迭代中,return 部分填充的字符串,直到最后你有一个完全填充的字符串,没有占位符剩余。

我将 R.reduce 与 R.add 一起使用来模拟我的解决方案,即:

R.reduce(R.add, 0, [1,2,3,4,5]);

这对我来说似乎符合相同的模式,除非我弄错了。

这是 R.reduce 的调用,它没有按预期工作:

R.reduce(rp, place_holders, pairs)

λ R.reduce(rp, place_holders, pairs);
TypeError: str.replace is not a function
    at replace (/Users/devuser/.nvm/versions/node/v10.13.0/lib/node_modules/ramda-repl/node_modules/ramda/src/replace.js:25:14)
    at Object.f3 [as replace] (/Users/devuser/.nvm/versions/node/v10.13.0/lib/node_modules/ramda-repl/node_modules/ramda/src/internal/_curry3.js:35:16)
    at XWrap.rp [as f] (repl:2:36)
    at XWrap.module.exports.XWrap.@@transducer/step (/Users/devuser/.nvm/versions/node/v10.13.0/lib/node_modules/ramda-repl/node_modules/ramda/src/internal/_xwrap.js:10:17)
    at _arrayReduce (/Users/devuser/.nvm/versions/node/v10.13.0/lib/node_modules/ramda-repl/node_modules/ramda/src/internal/_reduce.js:11:36)
    at _reduce (/Users/devuser/.nvm/versions/node/v10.13.0/lib/node_modules/ramda-repl/node_modules/ramda/src/internal/_reduce.js:44:14)
    at Object.f3 [as reduce] (/Users/devuser/.nvm/versions/node/v10.13.0/lib/node_modules/ramda-repl/node_modules/ramda/src/internal/_curry3.js:35:16)
λ 

我在 Ramda repl 中创建了一个代码片段:ramda-reply code snippet

你非常接近,你只是调换了 reducer 函数中的两个参数。而不是这个:

let rp = (pair, msg) => { return R.replace(pair.Key, pair.Value, msg); }

这样做:

let rp = (msg, pair) => { return R.replace(pair.Key, pair.Value, msg); }

const place_holders = "GREETINGS-{year}-{mm-month}-{dd-day} - HELLO WORLD";
let fields = ['{year}', '{mm-month}', '{dd-day}'];
let vals = ['2016', '06', '23'];

let tr = (k, v) => { return { Key: k, Value: v } }

let pairs = R.zipWith(tr, fields, vals);

let rp = (msg, pair) => { return R.replace(pair.Key, pair.Value, msg);}

console.log(R.reduce(rp, place_holders, pairs))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

正如 Nicholas Tower 指出的那样,这个错误是一个简单的换位。我的版本是将命令式代码包装在一个函数中,所以我可能会这样写:

const fillIn = (fields, place_holders, vals) => reduce(
  (str, {Key, Val}) => replace(Key, Val, str), 
  place_holders, 
  zipWith((Key, Val) => ({Key, Val}), fields, vals)
)

const place_holders = "GREETINGS-{year}-{mm-month}-{dd-day} - HELLO WORLD";
let fields = ['{year}', '{mm-month}', '{dd-day}'];
let vals = ['2016', '06', '23'];

console.log(fillIn(fields, place_holders, vals))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>
const {reduce, replace, zipWith} = R
</script>

如果我想部分应用字段(可能还有占位符),我也可以将其包装在 curry 中。


更新:您可能还想考虑不同的数据结构。使用两个数组来存储基于共享索引的成对值通常很脆弱。此结构以更稳健的方式保存相同的信息:

{year: '2016', 'mm-month': '06', 'dd-day': '23'}

使用它还可以让您使用正则表达式更动态地处理占位符:

const fillIn = (placeHolder, context) => placeHolder.replace(
  /\{([^}]+)\}/g, 
  (s, key) => key in context ? context[key] : `{${key}}`
)

const context = {year: '2016', 'mm-month': '06', 'dd-day': '23'}

console.log(fillIn("GREETINGS-{year}-{mm-month}-{dd-day} - HELLO WORLD", context))
console.log(fillIn("GREETINGS-{foobar}-{mm-month}-{dd-day} - HELLO WORLD", context))

这有一个缺点。如果您在字符串中嵌套了大括号,这将失败。我实际上会使用更像内置字符串模板的模板,"GREETINGS-${year}-${mm-month}-${dd-day} - HELLO WORLD" 以避免这个问题,并使它们更好地脱颖而出。额外的 $ 也必须添加到正则表达式中,但这很简单:/$\{([^}]+)\}/g.

这个技巧可能对你没有帮助。如果这些数据结构来自外部系统,那么您就会陷入困境。但是,如果您可以控制它们,请考虑将其视为您现有版本的一个可能更灵活、更强大的版本。