我怎样才能用 Ramda 深度映射对象

How can I deeply map over object with Ramda

我正在尝试查找所有 "template values" 例如{ template: 'Date: <now>'} 使用 map 函数得到这个基本行为:

deepMap(mapFn, {a: 1, b: { c: 2, d: { template: 'Date: <now>'}}})

>> {a: 1, b: { c: 2, d: 'Date: 13423234232'}}

这就是我目前所拥有的。模板对象的插值确实发生,但它不会替换值。

const obj = {a: 1, b: { c: 2, d: { template: 'Date: <now>'}}};

const deepMap = (fn, xs) =>
  mapObjIndexed(
    (val, key, obj) =>
      or(is(Array, val), is(Object, val))
        ? deepMap(fn, fn(val)) 
        : fn(val),
    xs
  );

const checkFn = ({ template }) => template;
const transformFn = (val, key) => {
  const interpolated = val.template.replace('<now>', Date.now())
  console.log(interpolated);
  return interpolated;
};

const mapFn = n =>
  checkFn(n)
    ? transformFn(n)
    : n;
console.clear();
deepMap(mapFn, obj);

>> {"a": 1, "b": {"c": 2, "d": {}}}

问题是您再次对映射值调用 deepMap - 但映射值不再是对象,而是字符串。

or(is(Array, val), is(Object, val))
        ? deepMap(fn, fn(val)) 
        : fn(val),

如果 val 是 { template: 'Date: <now>'},val 是一个对象并且可以被深度映射,但是 fn(val) 是一个字符串 ("Date: 123123123"),应该简单地返回它。一种解决方案是 is 检查映射值,而不是原始值:

(val, key) => {
      const mappedVal = fn(val);
      return or(is(Array, mappedVal), is(Object, mappedVal))
        ? deepMap(fn, mappedVal) 
        : mappedVal;
 },

另一种可能性是检查映射函数是否返回了原始值之外的其他内容,并且在这种情况下不要递归。

像这样的东西应该可以工作:

const {map, has, is} = R

const transformTemplate = ({template}) => template.replace('<now>', Date.now())

const deepMap = (xs) => map(x => has('template', x) 
  ? transformTemplate(x)
  : is(Object, x) || is(Array, x)
    ? deepMap(x)
    : x, xs)

const result = deepMap({a: 1, b: { c: 2, d: { template: 'Date: <now>'}}})
// => {a: 1, b: {c: 2, d: "Date: 1542046789004"}}

console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

如果想传入转换函数,可以稍微改成

const deepMap = (transformer, xs) => map(x => has('template', x) 
  ? transformer(x)
  : is(Object, x) || is(Array, x)
    ? deepMap(transformer, x)
    : x, xs)

const result = deepMap(transformTemplate, {a: 1, b: { c: 2, d: { template: 'Date: <now>'}}})

当然,您可以根据需要将其包装在 curry 中。

我现在没有时间研究为什么这种乍一看正确的方法不起作用。我希望这很简单:

const deepMap = map(cond([
  [has('template'), transformTemplate],
  [is(Object), deepMap],
  [is(Array), deepMap],
  [T, identity]
]))