JS - 深层地图功能

dth*_*ree 15 javascript json dictionary

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]
Run Code Online (Sandbox Code Playgroud)

我正在寻找一个类似的函数,可以迭代嵌套对象或深度映射.经过大量的搜索,我真的找不到这个.我能找到的东西是采摘深层物体,但不能遍历深层物体的每个值.

像这样的东西:

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;
})
Run Code Online (Sandbox Code Playgroud)

怎么会这样做?

样本输出

{
  one: 1,
  two: [
    { foo: 'bobcat' },
    { foos: ['bobcat', 'a', 'r', 's'] },
  ],
  three: [1, 2, 3]
}
Run Code Online (Sandbox Code Playgroud)

meg*_*wac 17

这是使用变换的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
});
Run Code Online (Sandbox Code Playgroud)


Aln*_*tak 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

演示http://jsfiddle.net/alnitak/0u96o2np/

现在,通过使用Array.prototype.map阵列盒的ES5标准略微缩短了编辑

  • 我使用了你的函数,但我不得不稍微修改它以正确处理空值。在您对对象类型的所有比较中,我必须添加一个检查对象是否为空或未定义。因此,例如,`if (Array.isArray(obj))` 变为 `if(obj != null && Array.isArray(obj))`,`(typeof val === 'object')` 变为 `(val != null && typeof val === 'object')` 等等。 (2认同)

McM*_*ath 5

我发布了一个名为Deep Map 的包来满足这一需求。如果您想映射对象的键而不是值,我编写了Deep Map Keys

\n\n

值得注意的是,这里的答案都没有解决一个重大问题:循环引用。这是一个处理这些 rotters 的有点幼稚的实现:

\n\n
function deepMap(value, mapFn, thisArg, key, cache=new Map()) {\n  // Use cached value, if present:\n  if (cache.has(value)) {\n    return cache.get(value);\n  }\n\n  // If value is an array:\n  if (Array.isArray(value)) {\n    let result = [];\n    cache.set(value, result); // Cache to avoid circular references\n\n    for (let i = 0; i < value.length; i++) {\n      result.push(deepMap(value[i], mapFn, thisArg, i, cache));\n    }\n    return result;\n\n  // If value is a non-array object:\n  } else if (value != null && /object|function/.test(typeof value)) {\n    let result = {};\n    cache.set(value, result); // Cache to avoid circular references\n\n    for (let key of Object.keys(value)) {\n      result[key] = deepMap(value[key], mapFn, thisArg, key, cache);\n    }\n    return result;\n\n  // If value is a primitive:\n  } else {\n    return mapFn.call(thisArg, value, key);\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

你可以这样使用它:

\n\n
class Circlular {\n  constructor() {\n    this.one = \'one\';\n    this.arr = [\'two\', \'three\'];\n    this.self = this;\n  }\n}\n\nlet mapped = deepMap(new Circlular(), str => str.toUpperCase());\n\nconsole.log(mapped.self.self.self.arr[1]); // \'THREE\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

当然,上面的例子是在ES2015中。请参阅Deep Map以获取更优化的 \xe2\x80\x93,尽管不太简洁 \xe2\x80\x93 使用TypeScript编写的 ES5 兼容实现。

\n


Geo*_*rin 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 当心,`typeof null ==='object'`,所以测试`typeof obj ==='object'`是不够的 (4认同)