简体   繁体   中英

How can I deeply map over object with Ramda

I'm trying to find all "template values" eg { template: 'Date: <now>'} using a map function to get this basic behaviour:

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

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

This is what I have so far. The interpolation of the template object does happen, but it does not replace the value.

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": {}}}

The problem is you are calling deepMap on the mapped value again - but the mapped value isn't an object anymore, but a string.

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

In case val is { template: 'Date: <now>'} , val is an object and could be deep-mapped, but fn(val) is a String ( "Date: 123123123" ) which should simply be returned. One solution is to make the is checks on the mapped value, not the original value:

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

Another possibility would be to check whether the map-function returned something else than the original value and don't recurse in this case.

Something like this should work:

 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> 

If you wanted to pass in the transformation function, you can change it slightly to

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>'}}})

And of course you can wrap that in curry if you like.

I don't have time right now to investigate why this approach, which looks right at first glance, doesn't work. I'm hoping it's something simple:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM