JS - deep map function

后端 未结 8 518
挽巷
挽巷 2020-12-06 04:48

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         


        
相关标签:
8条回答
  • 2020-12-06 05:19

    Here's my version - slightly lengthy so I expect it can be shortened, but works with arrays and objects and no external dependencies:

    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;
        }
    }
    

    demo at http://jsfiddle.net/alnitak/0u96o2np/

    EDIT slightly shortened now by using ES5-standard Array.prototype.map for the array case

    0 讨论(0)
  • 2020-12-06 05:23

    Here's a Lodash solution using transform

    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
    });
    
    0 讨论(0)
  • 2020-12-06 05:23

    If I understand correctly, here's an example, using recursion:

    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
      },{})
    }
    

    Then you can use it like so:

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

    Note that the mapping function has to be aware of the types, otherwise you'll get unexpected results. So you'd have to check the type in the mapping function if nested properties can have mixed types.

    For arrays you could do:

    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] } } }
    

    Note that the callback is function(value, key) so it works better with composition.

    0 讨论(0)
  • 2020-12-06 05:23

    es5 underscore.js version, supports arrays (integer keys) and objects:

    _.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);
        }
    };
    
    0 讨论(0)
  • 2020-12-06 05:25

    Here is a clean ES6 version:

    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;
    }
    
    0 讨论(0)
  • 2020-12-06 05:25

    Here's the function I just worked out for myself. I'm sure there's a better way to do this.

    // 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;
    });
    

    I cheated on using underscore.

    0 讨论(0)
提交回复
热议问题