Рекурсия есть:
const replaceValues = (val, test, replacer, key) =>
test(key, val)
? replacer(val)
: val instanceof Object
? Object.entries(val).reduce((acc, [ k, v ]) => (
acc[k] = replaceValues(v, test, replacer, k),
acc
), val.constructor())
: val;
Рекурсии нет:
function replaceValues(val, test, replacer) {
const stack = [];
const clones = new Map;
const getClone = (v, k) =>
test(k, v)
? replacer(v)
: v instanceof Object
? (clones.has(v) || stack.push([ v, clones.set(v, v.constructor()).get(v) ]),
clones.get(v))
: v;
if (val instanceof Object) {
for (getClone(val); stack.length;) {
const [ source, target ] = stack.pop();
for (const k in source) if (Object.hasOwn(source, k)) {
target[k] = getClone(source[k], k);
}
}
}
return getClone(val);
}
Как применять в вашем случае:
const newData = replaceValues(
data,
k => k?.endsWith('Date'),
v => v.replace(/(\d+)-(\d+)-/, '$2.$1.')
);