map function for objects (instead of arrays)
我有一个目标:
1 | myObject = { 'a': 1, 'b': 2, 'c': 3 } |
我正在寻找一种类似于
1 2 3 4 5 | newObject = myObject.map(function (value, label) { return value * value; }); // newObject is now { 'a': 1, 'b': 4, 'c': 9 } |
对于对象,javascript是否具有这样的
1 2 3 4 5 6 7 8 | 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 } |
但您可以使用
1 2 3 4 5 6 7 8 9 10 | 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 } |
更新
很多人都提到以前的方法不会返回新对象,而是对对象本身进行操作。为此,我想添加另一个解决方案,该解决方案返回一个新对象并保留原来的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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': 1, 'b': 4, 'c': 6 } console.log(myObject); // => { 'a': 1, 'b': 2, 'c': 3 } |
在普通JS(ES6/ES2015)中,一个带有即时变量分配的单行程序如何?
使用排列运算符和计算的键名语法:
1 | let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]}))); |
杰斯宾
使用Reduce的另一个版本:
1 | let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {}); |
杰斯宾
作为函数的第一个示例:
1 2 3 4 | 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); |
杰斯宾
如果要以函数样式递归映射嵌套对象,可以这样做:
1 2 3 4 5 6 | 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. ,{}) |
杰斯宾
或者更具强制性的,像这样:
1 2 3 4 5 6 7 | 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; }; |
杰斯宾
由于ES7/ES2016,您可以使用
1 | let newObj = Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v}))); |
继承的属性和原型链:
在一些罕见的情况下,您可能需要映射类对象,该类对象在其原型链上保存继承对象的属性。在这种情况下,
下面是一个继承另一个对象属性的对象的例子,以及
1 2 3 4 5 6 7 8 9 10 11 | 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' } |
杰斯宾
不过,请帮我一个忙,不要继承遗产。-)
没有本地方法,但是lodash mapvalues将出色地完成这项工作
1 2 | _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; }); // → { 'a': 3, 'b': 6, 'c': 9 } |
写一篇很容易:
1 2 3 4 5 6 7 8 | 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; } |
示例代码:
1 2 3 4 5 6 | > 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 } |
注意:这个版本还允许(可选)为回调设置
edit-更改为删除对
您可以对返回的键数组使用
1 2 3 4 5 6 | var myObject = { 'a': 1, 'b': 2, 'c': 3 }, newObject = {}; Object.keys(myObject).forEach(function (key) { var value = myObject[key]; newObject[key] = value * value; }); |
或者以更模块化的方式:
1 2 3 4 5 6 7 8 9 | 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; }); |
请注意,
这是直的bs,JS社区中的每个人都知道它。应该有这样的功能:
1 2 3 4 5 | 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} |
这是NA吗?ve实施:
1 2 3 4 5 6 7 8 9 10 | 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; }; |
必须一直自己执行这项任务,这是非常烦人的;)
如果您想要一些更复杂的东西,它不会干扰对象类,请尝试以下操作:
1 2 3 4 5 6 7 8 9 10 11 | 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; }); |
但是将这个映射函数添加到对象中是安全的,只是不要添加到object.prototype中。
1 2 | Object.map = ... // fairly safe Object.prototype.map ... // not ok |
我来这里是想找到一个对象映射到数组的答案,结果得到了这个页面。如果您来这里是为了寻找与我相同的答案,这里是如何映射和反对一个数组。
可以使用map从对象返回新数组,如下所示:
1 2 3 | var newObject = Object.keys(myObject).map(function(key) { return myObject[key]; }); |
接受的答案有两个缺点:
- 它误用了
Array.prototype.reduce ,因为reducing意味着改变复合类型的结构,这种情况下不会发生。 - 它不是特别可重复使用的
ES6/ES2015功能方法
请注意,所有函数都是以课程形式定义的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // 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)); |
- 在A行中,
o 被重新分配。由于javascript通过共享来传递引用值,因此会生成o 的一个浅拷贝。我们现在能够在omap 内突变o ,而不在母体范围内突变o 。 - 在B行中,
map 的返回值被忽略,因为map 执行o 的突变。由于这种副作用仍在omap 内,在父级范围内不可见,因此完全可以接受。
这不是最快的解决方案,而是一个声明性的、可重用的解决方案。以下是与一行代码相同的实现,简洁但不可读:
1 | const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o); |
附录-为什么对象在默认情况下不可重复?
ES2015指定了迭代器和ITerable协议。但对象仍然不可重复,因此无法映射。原因是数据和程序级别的混合。
以获得最佳性能。
如果对象不经常更改,但需要经常迭代,我建议使用本机映射作为缓存。
1 2 3 4 5 6 7 8 9 10 11 | // example object var obj = {a: 1, b: 2, c: 'something'}; // caching map var objMap = new Map(Object.entries(obj)); // fast iteration on Map object objMap.forEach((item, key) => { // do something with an item console.log(key, item); }); |
object.entries已经可以在chrome、edge、firefox和beta-opera中使用,因此它是一个未来的功能。它来自ES7,所以polyfill它https://github.com/es-timsts/object.entries,在IE中它不起作用。
最低版本(ES6):
1 | Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {}) |
您可以在数组上使用
使用JavaScript(ES6)
1 2 3 4 5 6 7 | var obj = { 'a': 2, 'b': 4, 'c': 6 }; Object.entries(obj).map( v => obj[v[0]] *= v[1] ); console.log(obj); //it will log as {a: 4, b: 16, c: 36} var obj2 = { 'a': 4, 'b': 8, 'c': 10 }; Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] ); console.log(obj2); //it will log as {a: 16, b: 64, c: 100} |
使用jQuery
1 2 3 4 5 | var ob = { 'a': 2, 'b': 4, 'c': 6 }; $.map(ob, function (val, key) { ob[key] *= val; }); console.log(ob) //it will log as {a: 4, b: 16, c: 36} |
也可以使用其他循环,如
1 2 3 4 | $.each(ob,function (key, value) { ob[key] *= value; }); console.log(ob) //it will also log as {a: 4, b: 16, c: 36} |
这个答案被改写为一个强大的组合体
直观地说,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | const identity = x => x const first = (a, b) => a const mapReduce = (m = identity, r = first) => (acc, x) => r (acc, m (x)) const omap = (o = {}, f = identity) => Object.keys (o) .reduce ( mapReduce ( k => ({ [k]: f (o [k]) }) // Object.map , Object.assign // Object.concat ) , {} // Object.empty ) const square = x => x * x const data = { a : 1, b : 2, c : 3 } console.log (omap (data, square)) // { a : 1, b : 4, c : 9 } |
注意,我们实际上必须编写的程序的唯一部分是映射实现本身-
1 | k => ({ [k]: f (o [k]) } |
也就是说,给定一个已知的对象
如果我们首先提取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | const oreduce = (o, f = first, acc = {}) => Object.keys (o) .reduce ( mapReduce ( k => [ k, o[k] ] , f ) , acc ) const omap = (o = {}, f = identity) => oreduce ( o , mapReduce ( ([ k, v ]) => ({ [k]: f (v) }) , Object.assign ) , {} ) |
一切都是一样的,但是现在可以在更高的层次上定义
你在这里看不到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var myMap = function ( obj, callback ) { var result = {}; for ( var key in obj ) { if ( Object.prototype.hasOwnProperty.call( obj, key ) ) { if ( typeof callback === 'function' ) { result[ key ] = callback.call( obj, obj[ key ], key, obj ); } } } return result; }; var myObject = { 'a': 1, 'b': 2, 'c': 3 }; var newObject = myMap( myObject, function ( value, key ) { return value * value; }); |
如果您对
1 2 3 4 5 6 | var source = { a: 1, b: 2 }; function sum(x) { return x + x } source.map(sum); // returns { a: 2, b: 4 } source.map(undefined, sum); // returns { aa: 1, bb: 2 } source.map(sum, sum); // returns { aa: 2, bb: 4 } |
我把它作为谷歌搜索的第一个项目,试图学习如何做到这一点,并认为我会与其他Folsk分享我最近发现的解决方案,它使用了不可变的NPM包。
我认为分享这一点很有趣,因为不可变在他们自己的文档中使用了操作的确切情况-以下不是我自己的代码,而是从当前不可变的JS文档中提取的:
1 2 3 4 | const { Seq } = require('immutable') const myObject = { a: 1, b: 2, c: 3 } Seq(myObject).map(x => x * x).toObject(); // { a: 1, b: 4, c: 9 } |
并不是说seq有其他属性("seq描述了一个懒惰的操作,允许它们通过不创建中间集合来高效地链式使用所有的高阶收集方法(如map和filter)),也不是说其他一些不可变的JS数据结构也可以非常高效地完成这项工作。
当然,任何使用这种方法的人都必须阅读以下文件:
https://facebook.github.io/immutable-js网站/
基于@amberlamps answer,这里有一个实用函数(作为评论,它看起来很难看)
1 2 3 4 5 6 | function mapObject(obj, mapFunc){ return Object.keys(obj).reduce(function(newObj, value) { newObj[value] = mapFunc(obj[value]); return newObj; }, {}); } |
其用途是:
1 2 3 4 5 | var obj = {a:1, b:3, c:5} function double(x){return x * 2} var newObj = mapObject(obj, double); //=> {a: 2, b: 6, c: 10} |
javascript刚刚得到了新的
1 2 3 4 5 6 7 | const myObject = { a: 1, b: 2, c: 3 } const myNewObject = Object.fromEntries( Object .entries(myObject) .map(([key, value]) => ([key, value * value])) ) console.log(myNewObject) |
上面的代码将对象转换成一个嵌套的数组(
这个模式最酷的一点是,您现在可以轻松地在映射时考虑对象键。
文档Object.fromEntries() Object.entries()
浏览器支持
目前只有这些浏览器/引擎支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | var myObject = { 'a': 1, 'b': 2, 'c': 3 }; Object.prototype.map = function(fn){ var oReturn = {}; for (sCurObjectPropertyName in this) { oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName); } return oReturn; } Object.defineProperty(Object.prototype,'map',{enumerable:false}); newObject = myObject.map(function (value, label) { return value * value; }); // newObject is now { 'a': 1, 'b': 4, 'c': 9 } |
首先,使用object.entries(collection)转换htmlcollection。然后它是一个不可更改的对象,您现在可以在上面使用.map方法。
1 | Object.entries(collection).map(...) |
参考https://medium.com/@js-tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31
我的回答很大程度上是基于这里最高评级的回答,希望每个人都能理解(在我的Github上也有相同的解释)。这就是为什么他对地图的实施有效:
1 2 | Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' + ')'); |
函数的目的是获取一个对象,并使用所有对象(对象和数组类似)都可用的方法修改对象的原始内容,而不返回数组。几乎JS中的所有内容都是一个对象,因此,继承管道的更深处的元素在技术上可能会使用那些向上的元素(相反,它会出现)。
这样做的原因是.map函数返回一个数组,要求您提供一个显式或隐式的数组返回,而不是简单地修改一个现有对象。实际上,通过使用object.keys,您可以欺骗程序认为对象是一个数组,它允许您使用map函数,使其作用于各个键所关联的值(实际上我意外地返回了数组,但修复了它)。只要没有一个正常意义上的返回,就不会有数组与原始对象stil一起创建,并按照编程进行修改。
这个特定的程序接受一个名为images的对象,并获取其键的值,并附加URL标记以在另一个函数中使用。原件是:
1 2 3 4 5 6 | var images = { snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305', sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east- Matanzas-city- Cuba-20170131-1080.jpg', rain: 'https://i.pinimg.com/originals/23/d8 /ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' }; |
…修改如下:
1 2 3 4 5 6 7 | var images = { snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'), sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning- east-Matanzas-city- Cuba-20170131-1080.jpg'), rain: url('https://i.pinimg.com/originals/23/d8 /ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg') }; |
对象的原始结构保持不变,只要没有返回,就允许正常的属性访问。不要让它返回一个正常的数组,一切都会好起来的。目标是将原始值(图像[键])重新分配给需要的内容,而不是其他任何内容。据我所知,为了防止数组输出,必须重新分配图像[键],并且没有隐式或显式请求返回数组(变量分配会这样做,并且会来回地发生故障)。
编辑:
为了避免修改原始对象,将处理与新对象创建相关的其他方法(为了避免意外地将数组创建为输出,重新分配似乎仍然是必要的)。这些函数使用箭头语法,如果您只想创建一个新的对象以供将来使用,则使用箭头语法。
1 2 3 4 5 6 | const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => { result[key] = mapFn(obj)[key]; return result; }, {}); var newImages = mapper(images, (value) => value); |
这些功能的工作方式如下:
mapfn接受稍后要添加的函数(在本例中(value)=>value),并简单地返回在mapfn(obj)【key】中作为该键值存储的任何内容(如果您像他那样更改返回值,则乘以2)。
然后在result[key]=mapfn(obj)[key]中重新定义与键关联的原始值
并返回对结果执行的操作(位于.reduce函数末尾启动的括号中的累加器)。
所有这些操作都是在所选对象上执行的,但仍然不能对返回的数组进行隐式请求,并且仅在重新分配值时有效(据我所知)。这需要一些心理上的练习,但是可以减少上面所看到的代码行。输出与如下所示完全相同:
1 2 3 4 5 | {snow:"https://www.trbimg.com/img-5aa059f5/turbine/bs- md-weather-20180305", sunny:"http://www.cubaweather.org/images/weather- photos/l…morning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain: "https://i.pinimg.com/originals/23/d8 /ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"} |
记住这对非数字有效。只需返回mapfn函数中的值,就可以复制任何对象。
I specifically wanted to use the same function that I was using for arrays for a single object, and wanted to keep it simple. This worked for me:
1 | var mapped = [item].map(myMapFunction).pop(); |
嘿,写了一个小的映射函数,可能会有所帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 | function propertyMapper(object, src){ for (var property in object) { for (var sourceProp in src) { if(property === sourceProp){ if(Object.prototype.toString.call( property ) === '[object Array]'){ propertyMapper(object[property], src[sourceProp]); }else{ object[property] = src[sourceProp]; } } } } } |
您可以使用简单的for-in循环遍历有序对。我使用
第二种方法以与第一种方法相似的方式创建地图,但可能比这里的其他一些答案要慢得多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var myObject = { 'a': 1, 'b': 2, 'c': 3 } //Doesn't create a map, just applies the function to a specific element function getValue(key) { for (var k in myObject) { if (myObject.hasOwnProperty(key)) { var value = myObject[key] return value * value; //stops iteration } } } //creates a map (answers question, but above function is better in some situations) var newObject = {}; makeMap(); function makeMap() { for (var k in myObject) { var value = myObject[k]; newObject[k] = value * value; } } console.log(newObject); //mapped array |
1 2 3 | Input: <input id="input" value="" placeholder="a, b or c"> Output:<input id="output"> <button onclick="output.value=getValue(input.value)">Get value</button> |
为了更紧密地响应OP所要求的内容,OP需要一个对象:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
有一个地图方法
similar to Array.prototype.map that would be used as follows:
1
2
3
4 newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
IMHO的最佳答案(以"接近要求"+"不需要"5,6,7来衡量)是:
1 2 3 4 5 6 7 8 9 10 | myObject.map = function mapForObject(callback) { var result = {}; for(var property in this){ if(this.hasOwnProperty(property) && property !="map"){ result[property] = callback(this[property],property,this); } } return result; } |
以上代码避免有意使用任何语言功能,仅在最新的ecmascript版本中可用。有了以上代码,问题就可以解决了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | myObject = { 'a': 1, 'b': 2, 'c': 3 }; myObject.map = function mapForObject(callback) { var result = {}; for(var property in this){ if(this.hasOwnProperty(property) && property !="map"){ result[property] = callback(this[property],property,this); } } return result; } newObject = myObject.map(function (value, label) { return value * value; }); console.log("newObject is now",newObject); |
此处为备选测试代码
除了受到一些人的反对之外,还可以像这样将解决方案插入到原型链中。
1 2 3 4 5 6 7 8 9 10 | Object.prototype.map = function(callback) { var result = {}; for(var property in this){ if(this.hasOwnProperty(property)){ result[property] = callback(this[property],property,this); } } return result; } |
在仔细监督下进行的某些操作不应产生任何不良影响,也不应影响其他对象(即阵列的
我需要一个允许修改密钥的版本(基于@amberlamps和@yonatanmn答案);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var facts = [ // can be an object or array - see jsfiddle below {uuid:"asdfasdf",color:"red <hr>[cc lang="javascript"]settings = { message_notification: { value: true, is_active: true, slug: 'message_notification', title: 'Message Notification' }, support_notification: { value: true, is_active: true, slug: 'support_notification', title: 'Support Notification' }, }; let keys = Object.keys(settings); keys.map(key=> settings[key].value = false ) console.log(settings) |
如果有人正在寻找将对象映射到新对象或数组的简单解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Maps an object to a new object by applying a function to each key+value pair. // Takes the object to map and a function from (key, value) to mapped value. const mapObject = (obj, fn) => { const newObj = {}; Object.keys(obj).forEach(k => { newObj[k] = fn(k, obj[k]); }); return newObj; }; // Maps an object to a new array by applying a function to each key+value pair. // Takes the object to map and a function from (key, value) to mapped value. const mapObjectToArray = (obj, fn) => ( Object.keys(obj).map(k => fn(k, obj[k])) ); |
这可能不适用于所有对象或所有映射函数,但它适用于简单的浅对象和简单的映射函数,这正是我所需要的。
1 2 3 4 5 | const orig = { 'a': 1, 'b': 2, 'c': 3 } const result = _.transform(orig, (r, v, k) => r[k.trim()] = v * 2); console.log(result); |
1 | <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"> |
使用new.transform()转换对象。
另一个不同的做法是使用自定义的JSON字符串化函数,该函数也可以处理深层对象。如果您打算将它作为JSON发布到服务器上,这可能很有用。
1 2 3 4 5 | const obj = { 'a': 1, 'b': 2, x: {'c': 3 }} const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v) console.log(json) console.log('back to json:', JSON.parse(json)) |