Javascript 对象的映射函数(而不是数组)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14810506/
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
map function for objects (instead of arrays)
提问by Randomblue
I have an object:
我有一个对象:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
I am looking for a native method, similar to Array.prototype.mapthat would be used as follows:
我正在寻找一种与本机方法类似的方法,Array.prototype.map如下所示:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
Does JavaScript have such a mapfunction for objects? (I want this for Node.JS, so I don't care about cross-browser issues.)
JavaScript 有这样的map对象函数吗?(我想要这个用于 Node.JS,所以我不关心跨浏览器的问题。)
回答by Amberlamps
There is no native mapto the Objectobject, but how about this:
map该Object对象没有本机,但是如何:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
Object.keys(myObject).map(function(key, index) {
myObject[key] *= 2;
});
console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }
But you could easily iterate over an object using for ... in:
但是您可以使用for ... in以下方法轻松迭代对象:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
for (var key in myObject) {
if (myObject.hasOwnProperty(key)) {
myObject[key] *= 2;
}
}
console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }
Update
更新
A lot of people are mentioning that the previous methods do not return a new object, but rather operate on the object itself. For that matter I wanted to add another solution that returns a new object and leaves the original object as it is:
很多人都提到,前面的方法不会返回一个新对象,而是对对象本身进行操作。就此而言,我想添加另一个解决方案,该解决方案返回一个新对象并将原始对象保持原样:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
return Object.keys(object).reduce(function(result, key) {
result[key] = mapFn(object[key])
return result
}, {})
}
var newObject = objectMap(myObject, function(value) {
return value * 2
})
console.log(newObject);
// => { 'a': 2, 'b': 4, 'c': 6 }
console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }
Array.prototype.reducereduces an array to a single value by somewhat merging the previous value with the current. The chain is initialized by an empty object {}. On every iteration a new key of myObjectis added with twice the key as the value.
Array.prototype.reduce通过将前一个值与当前值进行某种程度的合并,将数组缩减为单个值。该链由一个空对象初始化{}。在每次迭代中,myObject都会添加一个新的键,并将键作为值的两倍。
Update
更新
With new ES6 features, there is a more elegant way to express objectMap.
有了新的 ES6 特性,有一种更优雅的方式来表达objectMap.
const objectMap = (obj, fn) =>
Object.fromEntries(
Object.entries(obj).map(
([k, v], i) => [k, fn(v, k, i)]
)
)
const myObject = { a: 1, b: 2, c: 3 }
console.log(objectMap(myObject, v => 2 * v))
回答by Rotareti
How about a one liner with immediate variable assignment in plain JS (ES6 / ES2015) ?
在普通 JS ( ES6/ES2015) 中使用直接变量赋值的单行怎么样?
Making use of spread operatorand computed key namesyntax:
let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));
Another version using reduce:
使用reduce的另一个版本:
let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});
First example as a function:
作为函数的第一个示例:
const oMap = (o, f) => Object.assign({}, ...Object.keys(o).map(k => ({ [k]: f(o[k]) })));
// To square each value you can call it like this:
let mappedObj = oMap(myObj, (x) => x * x);
If you want to map a nested object recursivelyin a functionalstyle, it can be done like this:
如果要映射嵌套对象递归的功能性的风格,这是可以做到这样的:
const sqrObjRecursive = obj =>
Object.keys(obj).reduce(
(newObj, key) =>
obj[key] && typeof obj[key] === "object"
? { ...newObj, [key]: sqrObjRecursive(obj[key]) } // recurse.
: { ...newObj, [key]: obj[key] * obj[key] }, // square val.
{}
);
Or more imperatively, like this:
或者更重要的是,像这样:
const sqrObjRecursive = obj => {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === "object") obj[key] = sqrObjRecursive(obj[key]);
else obj[key] = obj[key] * obj[key];
});
return obj;
};
Since ES7 / ES2016you can use Object.entries()instead of Object.keys()e.g. like this:
从ES7 / ES2016 开始,您可以使用,Object.entries()而不是Object.keys()像这样:
let newObj = Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));
ES2019introduced Object.fromEntries(), which simplifies this even more:
ES2019引入了Object.fromEntries(),这进一步简化了这一点:
let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));
Inherited properties and the prototype chain:
继承属性和原型链:
In some rare situation you may need to map a class-likeobject which holds properties of an inherited object on its prototype-chain. In such cases Object.keys()won't work, because Object.keys()does not enumerate inheritedproperties. If you need to map inheritedproperties, you should use for (key in myObj) {...}.
在一些罕见的情况下,您可能需要映射一个类对象,该对象在其原型链上保存继承对象的属性。在这种情况下Object.keys()将不起作用,因为Object.keys()不枚举继承的属性。如果需要映射继承的属性,则应使用for (key in myObj) {...}.
Here is an example of an object which inherits the properties of another object and how Object.keys()doesn't work in such scenario.
这是一个对象的示例,该对象继承了另一个对象的属性,以及Object.keys()在这种情况下如何不起作用。
const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1); // One of multiple ways to inherit an object in JS.
// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2) // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}
console.log(Object.keys(obj2)); // Prints: an empty Array.
for (key in obj2) {
console.log(key); // Prints: 'a', 'b', 'c'
}
However, please do me a favor and avoid inheritance. :-)
但是,请帮我一个忙,避免继承。:-)
回答by Mario
No native methods, but lodash#mapValueswill do the job brilliantly
没有本地方法,但lodash#mapValues可以出色地完成这项工作
_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
回答by Alnitak
It's pretty easy to write one:
写一个很容易:
Object.map = function(o, f, ctx) {
ctx = ctx || this;
var result = {};
Object.keys(o).forEach(function(k) {
result[k] = f.call(ctx, o[k], k, o);
});
return result;
}
with example code:
使用示例代码:
> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
return v * v;
});
> r
{ a : 1, b: 4, c: 9 }
NB: this version also allows you to (optionally) set the thiscontext for the callback, just like the Arraymethod.
注意:此版本还允许您(可选)设置this回调的上下文,就像Array方法一样。
EDIT- changed to remove use of Object.prototype, to ensure that it doesn't clash with any existing property named mapon the object.
编辑- 更改为删除 , 的使用Object.prototype,以确保它不会与map对象上命名的任何现有属性发生冲突。
回答by Mattias Buelens
You could use Object.keysand then forEachover the returned array of keys:
您可以使用Object.keys然后forEach在返回的键数组上:
var myObject = { 'a': 1, 'b': 2, 'c': 3 },
newObject = {};
Object.keys(myObject).forEach(function (key) {
var value = myObject[key];
newObject[key] = value * value;
});
Or in a more modular fashion:
或者以更模块化的方式:
function map(obj, callback) {
var result = {};
Object.keys(obj).forEach(function (key) {
result[key] = callback.call(obj, obj[key], key, obj);
});
return result;
}
newObject = map(myObject, function(x) { return x * x; });
Note that Object.keysreturns an array containing only the object's own enumerable properties, thus it behaves like a for..inloop with a hasOwnPropertycheck.
请注意,Object.keys返回一个只包含对象自己的可枚举属性的数组,因此它的行为就像一个for..in带有hasOwnProperty检查的循环。
回答by Alexander Mills
This is straight bs, and everyone in the JS community knows it. There shouldbe this functionality:
这是直接bs,JS社区的每个人都知道。有应该是这样的功能:
const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);
console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}
here is the na?ve implementation:
这是天真的实现:
Object.map = function(obj, fn, ctx){
const ret = {};
for(let k of Object.keys(obj)){
ret[k] = fn.call(ctx || null, k, obj[k]);
});
return ret;
};
it is super annoying to have to implement this yourself all the time ;)
必须一直自己实现这个非常烦人;)
If you want something a little more sophisticated, that doesn't interfere with the Object class, try this:
如果你想要一些更复杂的东西,这不会干扰 Object 类,试试这个:
let map = function (obj, fn, ctx) {
return Object.keys(obj).reduce((a, b) => {
a[b] = fn.call(ctx || null, b, obj[b]);
return a;
}, {});
};
const x = map({a: 2, b: 4}, (k,v) => {
return v*2;
});
but it is safe to add this map function to Object, just don't add to Object.prototype.
但是把这个map函数加到Object里面是安全的,只是不要加到Object.prototype里。
Object.map = ... // fairly safe
Object.prototype.map ... // not ok
回答by JoeTron
I came here looking to find and answer for mapping an object to an array and got this page as a result. In case you came here looking for the same answer I was, here is how you can map and object to an array.
我来到这里寻找并回答将对象映射到数组的问题,结果得到了这个页面。如果您来到这里寻找与我相同的答案,这里是您如何映射和对象到数组的方法。
You can use map to return a new array from the object like so:
您可以使用 map 从对象返回一个新数组,如下所示:
var newObject = Object.keys(myObject).map(function(key) {
return myObject[key];
});
回答by JoeTron
The accepted answer has two drawbacks:
接受的答案有两个缺点:
- It misuses
Array.prototype.reduce, because reducing means to change the structure of a composite type, which doesn't happen in this case. - It is not particularly reusable
- 它误用了
Array.prototype.reduce,因为减少意味着改变复合类型的结构,这在这种情况下不会发生。 - 它不是特别可重用
An ES6/ES2015 functional approach
ES6/ES2015 函数式方法
Please note that all functions are defined in curried form.
请注意,所有函数都以柯里化形式定义。
// small, reusable auxiliary functions
const keys = o => Object.keys(o);
const assign = (...o) => Object.assign({}, ...o);
const map = f => xs => xs.map(x => f(x));
const mul = y => x => x * y;
const sqr = x => mul(x) (x);
// the actual map function
const omap = f => o => {
o = assign(o); // A
map(x => o[x] = f(o[x])) (keys(o)); // B
return o;
};
// mock data
const o = {"a":1, "b":2, "c":3};
// and run
console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));
- In line A
ois reassigned. Since Javascript passes reference values by sharing, a shallow copy ofois generated. We are now able to mutateowithinomapwithout mutatingoin the parent scope. - In line B
map's return value is ignored, becausemapperforms a mutation ofo. Since this side effect remains withinomapand isn't visible in the parent scope, it is totally acceptable.
- 在行 A 中
o被重新分配。由于使用Javascript通过参考值通过共享,一个浅的复制o产生。我们现在可以o在omap不改变o父作用域的情况下在内部进行变异。 - 在行 B
map的返回值被忽略,因为map执行了 的突变o。由于此副作用保留在omap父作用域内并且在父作用域中不可见,因此完全可以接受。
This is not the fastest solution, but a declarative and reusable one. Here is the same implementation as a one-line, succinct but less readable:
这不是最快的解决方案,而是一种声明式且可重用的解决方案。这是与一行代码相同的实现,简洁但可读性较差:
const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);
Addendum - why are objects not iterable by default?
附录 - 为什么默认情况下对象不可迭代?
ES2015 specified the iterator and iterable protocols. But objects are still not iterable and thus not mappable. The reason is the mixing of data and program level.
ES2015 指定了迭代器和可迭代协议。但是对象仍然不可迭代,因此不可映射。原因是数据和程序级别的混合。
回答by HaNdTriX
JavaScript just got the new Object.fromEntriesmethod.
JavaScript 刚刚获得了新Object.fromEntries方法。
Example
例子
function mapObject (obj, fn) {
return Object.fromEntries(
Object
.entries(obj)
.map(fn)
)
}
const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)
Explanation
解释
The code above converts the Object into an nested Array ([[<key>,<value>], ...]) wich you can map over. Object.fromEntriesconverts the Array back to an Object.
上面的代码将 Object 转换为一个嵌套的 Array ( [[<key>,<value>], ...]) ,您可以映射到它。Object.fromEntries将数组转换回对象。
The cool thing about this pattern, is that you can now easily take object keys into account while mapping.
这种模式很酷的一点是,您现在可以在映射时轻松考虑对象键。
Documentation
文档
Browser Support
浏览器支持
Object.fromEntriesis currently only supported by these browsers/engines, nevertheless there are polyfills available (e.g @babel/polyfill).
Object.fromEntries目前仅受这些浏览器/引擎支持,但仍有可用的 polyfill(例如@babel/polyfill)。
回答by Yukulélé
Minimal version (es6):
最小版本(es6):
Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})

