Underscore.js有一个非常有用的map
函数。
_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
我正在寻找一个可以迭代嵌套对象或深度映射的类似函数。经过大量的搜索,我无法真正找到这个。我能找到的东西是采摘一个深层对象,但不会迭代深层对象的每个值。
这样的事情:
deepMap({
one: 1,
two: [
{ foo: 'bar' },
{ foos: ['b', 'a', 'r', 's'] },
],
three: [1, 2, 3]
}, function(val, key) {
return (String(val).indexOf('b') > -1) ? 'bobcat' : val;
})
如何做到这一点?
{
one: 1,
two: [
{ foo: 'bobcat' },
{ foos: ['bobcat', 'a', 'r', 's'] },
],
three: [1, 2, 3]
}
答案 0 :(得分:17)
这是使用transform
的Lodash解决方案function deepMap(obj, iterator, context) {
return _.transform(obj, function(result, val, key) {
result[key] = _.isObject(val) /*&& !_.isDate(val)*/ ?
deepMap(val, iterator, context) :
iterator.call(context, val, key, obj);
});
}
_.mixin({
deepMap: deepMap
});
答案 1 :(得分:7)
这是我的版本 - 稍微冗长所以我希望它可以缩短,但是可以使用数组和对象而不需要外部依赖:
function deepMap(obj, f, ctx) {
if (Array.isArray(obj)) {
return obj.map(function(val, key) {
return (typeof val === 'object') ? deepMap(val, f, ctx) : f.call(ctx, val, key);
});
} else if (typeof obj === 'object') {
var res = {};
for (var key in obj) {
var val = obj[key];
if (typeof val === 'object') {
res[key] = deepMap(val, f, ctx);
} else {
res[key] = f.call(ctx, val, key);
}
}
return res;
} else {
return obj;
}
}
演示http://jsfiddle.net/alnitak/0u96o2np/
编辑现在使用ES5标准版Array.prototype.map
稍微缩短了数组大小写
答案 2 :(得分:5)
这是一个干净的ES6版本:
function mapObject(obj, fn) {
return Object.keys(obj).reduce(
(res, key) => {
res[key] = fn(obj[key]);
return res;
},
{}
)
}
function deepMap(obj, fn) {
const deepMapper = val => typeof val === 'object' ? deepMap(val, fn) : fn(val);
if (Array.isArray(obj)) {
return obj.map(deepMapper);
}
if (typeof obj === 'object') {
return mapObject(obj, deepMapper);
}
return obj;
}
答案 3 :(得分:3)
我已经发布了一个名为Deep Map的软件包来满足这一需求。如果你想要映射一个对象的键而不是它的值,我写了Deep Map Keys。
值得注意的是,这里的答案都没有解决一个重大问题:循环引用。这是一个有点天真的实现,处理这些转子:
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);
}
}
你可以像这样使用它:
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'
当然,上面的例子是在ES2015中。有关Deep Map中编写的更优化(尽管不那么简洁)与ES5兼容的实现,请参阅TypeScript。
答案 4 :(得分:2)
如果我理解正确,这是一个例子,使用递归:
var deepMap = function(f, obj) {
return Object.keys(obj).reduce(function(acc, k) {
if ({}.toString.call(obj[k]) == '[object Object]') {
acc[k] = deepMap(f, obj[k])
} else {
acc[k] = f(obj[k], k)
}
return acc
},{})
}
然后你可以像这样使用它:
var add1 = function(x){return x + 1}
var o = {
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
}
deepMap(add1, o)
//^ { a: 2, b: { c: 3, d: { e: 4 } } }
请注意,映射函数必须知道类型,否则您将获得意外结果。因此,如果嵌套属性可以具有混合类型,则必须检查映射函数中的类型。
对于数组,你可以这样做:
var map1 = function(xs){return xs.map(add1)}
var o = {
a: [1,2],
b: {
c: [3,4],
d: {
e: [5,6]
}
}
}
deepMap(map1, o)
//^ { a: [2,3], b: { c: [4,5], d: { e: [6,7] } } }
请注意,回调为function(value, key)
,因此在合成时效果会更好。
答案 5 :(得分:0)
这是我刚刚为自己制定的功能。我确信有更好的方法可以做到这一点。
// function
deepMap: function(data, map, key) {
if (_.isArray(data)) {
for (var i = 0; i < data.length; ++i) {
data[i] = this.deepMap(data[i], map, void 0);
}
} else if (_.isObject(data)) {
for (datum in data) {
if (data.hasOwnProperty(datum)) {
data[datum] = this.deepMap(data[datum], map, datum);
}
}
} else {
data = map(data, ((key) ? key : void 0));
}
return data;
},
// implementation
data = slf.deepMap(data, function(val, key){
return (val == 'undefined' || val == 'null' || val == undefined) ? void 0 : val;
});
我使用underscore
作弊。
答案 6 :(得分:0)
es5 underscore.js版本,支持数组(整数键)和对象:
_.recursiveMap = function(value, fn) {
if (_.isArray(value)) {
return _.map(value, function(v) {
return _.recursiveMap(v, fn);
});
} else if (typeof value === 'object') {
return _.mapObject(value, function(v) {
return _.recursiveMap(v, fn);
});
} else {
return fn(value);
}
};
答案 7 :(得分:0)
基于@megawac回复,我做了一些改进。
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;
});
此版本允许您替换偶数对象&amp;数组本身,并指定是否要使用iterateeExplore
参数探索遇到的每个对象/数组。
请参阅this fiddle了解演示