javascript JS - 深度地图功能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25333918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 04:22:47  来源:igfitidea点击:

JS - deep map function

javascriptjsondictionary

提问by dthree

Underscore.js has a very useful mapfunction.

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]

I am looking for a similar function that can iterate through nested objects, or deep mapping. After a ton of searching I can't really find this. What I can find is something to plucka deep object, but not iterate through every value of a deep object.

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

Something like this:

像这样的东西:

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

How would one do this?

怎么做呢?

Sample Output

样本输出

{
  one: 1,
  two: [
    { foo: 'bobcat' },
    { foos: ['bobcat', 'a', 'r', 's'] },
  ],
  three: [1, 2, 3]
}

回答by megawac

Here's a Lodash solution using 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
});

回答by Alnitak

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/

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

EDITslightly shortened now by using ES5-standard Array.prototype.mapfor the array case

EDIT现在通过使用 ES5 标准Array.prototype.map对数组情况略有缩短

回答by Geoffroy Warin

Here is a clean ES6 version:

这是一个干净的 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;
}

回答by McMath

I've published a package called Deep Mapto 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.

我发布了一个名为Deep Map的包来解决这个非常需要。如果你想映射一个对象的键而不是它的值,我写了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 Mapfor a more optimized – though less terse – ES5-compatible implementation written in TypeScript.

当然,上面的例子是在 ES2015 中的。请参阅Deep Map以获取用TypeScript编写的更优化(虽然不那么简洁)的 ES5 兼容实现。

回答by elclanrs

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.

请注意,回调是function(value, key)为了更好地与组合配合使用。

回答by dthree

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.

我在使用上作弊underscore

回答by Dmitriy Sintsov

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

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

回答by Alexandre Germain

Based on @megawac response, I made some improvements.

根据@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;
});

This version allows you to replace even objects & array themselves, and specify if you want to explore each objects/arrays encountered using the iterateeExploreparameter.

此版本允许您替换偶数对象和数组本身,并指定是否要使用iterateeExplore参数探索遇到的每个对象/数组。

See this fiddlefor a demo

请参阅此小提琴以获取演示