Underscore.js has a very useful map
function.
_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one: 1, two: 2, three: 3
Based on @megawac response, I made some improvements.
function mapExploreDeep(object, iterateeReplace, iterateeExplore = () => true) {
return _.transform(object, (acc, value, key) => {
const replaced = iterateeReplace(value, key, object);
const explore = iterateeExplore(value, key, object);
if (explore !== false && replaced !== null && typeof replaced === 'object') {
acc[key] = mapExploreDeep(replaced, iterateeReplace, iterateeExplore);
} else {
acc[key] = replaced;
}
return acc;
});
}
_.mixin({
mapExploreDeep: mapExploreDeep;
});
This version allows you to replace even objects & array themselves, and specify if you want to explore each objects/arrays encountered using the iterateeExplore
parameter.
See this fiddle for a demo
I've published a package called Deep Map to address this very need. And in case you want to map an object's keys rather than its values, I've written Deep Map Keys.
Notably, none of the answers on here address a significant problem: circular references. Here is a somewhat naive implementation that deals with these rotters:
function deepMap(value, mapFn, thisArg, key, cache=new Map()) {
// Use cached value, if present:
if (cache.has(value)) {
return cache.get(value);
}
// If value is an array:
if (Array.isArray(value)) {
let result = [];
cache.set(value, result); // Cache to avoid circular references
for (let i = 0; i < value.length; i++) {
result.push(deepMap(value[i], mapFn, thisArg, i, cache));
}
return result;
// If value is a non-array object:
} else if (value != null && /object|function/.test(typeof value)) {
let result = {};
cache.set(value, result); // Cache to avoid circular references
for (let key of Object.keys(value)) {
result[key] = deepMap(value[key], mapFn, thisArg, key, cache);
}
return result;
// If value is a primitive:
} else {
return mapFn.call(thisArg, value, key);
}
}
And you can use it like this:
class Circlular {
constructor() {
this.one = 'one';
this.arr = ['two', 'three'];
this.self = this;
}
}
let mapped = deepMap(new Circlular(), str => str.toUpperCase());
console.log(mapped.self.self.self.arr[1]); // 'THREE'
Of course, the example above is in ES2015. See Deep Map for a more optimized – though less terse – ES5-compatible implementation written in TypeScript.