在保留键的情况下对对象进行映射

154

underscore.js中的map函数,如果传入一个JavaScript对象,则返回一个由该对象值映射而来的值数组。

_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]

有没有一种方法可以使它保留键?也就是说,我想要一个返回结果为

{one: 3, two: 6, three: 9}

如果你和我一样,来到这里寻找一个像'mapValues'的函数,可以改变实际对象而不是返回一个新对象,请查看这个简单的解决方案:http://stackoverflow.com/questions/30894044/destructively-map-object-properties-function - Michahell
12个回答

256

使用Underscore

Underscore提供了一个函数_.mapObject,用于映射值并保留键。

_.mapObject({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; });

// => { one: 3, two: 6, three: 9 }

演示


使用 Lodash

Lodash 提供了一个名为 _.mapValues 的函数,可以映射值并保留键。

_.mapValues({ one: 1, two: 2, three: 3 }, function (v) { return v * 3; });

// => { one: 3, two: 6, three: 9 }

演示


有人努力将 _.mapValues 函数加入到 underscore 中:相关问题_.mapValues 的拉取请求。我希望这能够通过 :) - raffomania
我觉得你正在将一个OBJECT变成一个object,还是我疯了? - jsh
еңЁиҝҷз§Қжғ…еҶөдёӢпјҢ_.map()иҝ”еӣһзҡ„жҳҜ[['one', 3], ['two', 6], ['three', 9]]пјҢеҚідёҖдёӘеҢ…еҗ«ж•°з»„зҡ„ж•°з»„пјҢиҖҢ_.object()еҲҷе°Ҷе…¶иҪ¬жҚўеӣһеҜ№иұЎгҖӮ - Jezen Thomas

59

我在lodash这个类似于underscore的实用库中找到了所需的函数。

http://lodash.com/docs#mapValues

_.mapValues(object, [callback=identity], [thisArg])

通过对object的每个可枚举属性运行回调函数生成一个具有相同键值的对象。 回调函数绑定到thisArg并使用三个参数(value,key,object)调用。


20

这个纯JS版本怎么样 (ES6 / ES2015)?

let newObj = Object.assign(...Object.keys(obj).map(k => ({[k]: obj[k] * 3})));

jsbin

如果您想要对一个对象进行递归映射(映射嵌套对象),可以按如下方式进行:

const mapObjRecursive = (obj) => {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') obj[key] = mapObjRecursive(obj[key]);
    else obj[key] = obj[key] * 3;
  });
  return obj;
};

jsbin

自从 ES7 / ES2016 开始,你可以像这样使用 Object.entries 替代 Object.keys

let newObj = Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * 3})));

自从ES2019起,您可以使用Object.fromEntries方法。

let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => ([k, v * 3])))

这里将 entriesfromEntries 结合起来是一个非常聪明的方法。 - Drenai

19

var mapped = _.reduce({ one: 1, two: 2, three: 3 }, function(obj, val, key) {
    obj[key] = val*3;
    return obj;
}, {});

console.log(mapped);
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>


13

我知道这很老了,但是现在Underscore为对象提供了一个新的映射方法:

_.mapObject(object, iteratee, [context]) 

当然,你可以为数组和对象构建灵活的映射。

_.fmap = function(arrayOrObject, fn, context){
    if(this.isArray(arrayOrObject))
      return _.map(arrayOrObject, fn, context);
    else
      return _.mapObject(arrayOrObject, fn, context);
}

5
注意 lodash 用户:jdalton 已经决定因为这项更改而与 underscore.js 不兼容。lodash 将不再支持 mapObject 方法,请考虑使用 lodash 的 mapValues 方法代替。 - Mark Amery

4

我知道已经过了很长时间,但仍然缺少最明显的通过折叠(在js中称为reduce)解决方案,为了完整起见,我将在此留下它:

function mapO(f, o) {
  return Object.keys(o).reduce((acc, key) => {
    acc[key] = f(o[key])
    return acc
  }, {})
}

我对使用Lodash库感到满意,但程序员们正在使用这些库,却不知道如何在vanilla JS中实现这些功能。因此,我非常感激你的回答! - cyonder

3

_.map 返回的是一个数组而不是对象。

如果你想要一个对象,最好使用另一个函数,比如 each; 如果你真的想使用 map 函数,可以这样做:

Object.keys(object).map(function(value, index) {
   object[value] *= 3;
})

但是这很令人困惑,当看到map时,人们会期望得到一个数组作为结果,然后再进行处理。


我读文档时就有一种感觉,觉得在underscore.js中尝试这样做并不自然。我认为我的用例非常自然,为什么他们不支持呢? - xuanji
一个可能的原因是map用于改变一个产生数组输出的输入,你可以像@GG所写的那样组合_.object_.map,但这只是一个口味问题。 - Alberto Zaccagni

3

我想你需要一个mapValues函数(用于在对象的值上映射一个函数),这很容易自己实现:

mapValues = function(obj, f) {
  var k, result, v;
  result = {};
  for (k in obj) {
    v = obj[k];
    result[k] = f(v);
  }
  return result;
};

2
const mapObject = (obj = {}, mapper) =>
  Object.entries(obj).reduce(
    (acc, [key, val]) => ({ ...acc, [key]: mapper(val) }),
    {},
  );

我正准备自己添加这个答案。很高兴我滚动了一下! - Bill Criswell

1

使用lodash的_.map函数,就像循环一样实现这个功能

 var result={};
_.map({one: 1, two: 2, three: 3}, function(num, key){ result[key]=num * 3; });
console.log(result)

//output
{one: 1, two: 2, three: 3}

Reduce函数看起来很聪明,就像上面的答案一样

_.reduce({one: 1, two: 2, three: 3}, function(result, num, key) {
  result[key]=num * 3
  return result;
}, {});

//output
{one: 1, two: 2, three: 3}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接