我有一个目标:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
我正在寻找一个本地方法,类似于Array.prototype.map,可按如下方式使用:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
JavaScript是否有这样的对象映射函数?(我希望Node.JS使用这个,所以我不关心跨浏览器问题。)
公认的答案有两个缺点:
它误用了Array.prototype.reduce,因为reduce意味着改变复合类型的结构,这在这种情况下不会发生。它不是特别可重复使用
ES6/ES2015功能方法
请注意,所有函数都是以货币形式定义的。
//小型、可重复使用的辅助功能constkeys=o=>对象.keys(o);constassign=(…o)=>对象.assign({},…o);const-map=f=>xs=>xs.map(x=>f(x));常量mul=y=>x=>x*y;常量sqr=x=>mul(x)(x);//实际地图功能常量omap=f=>o=>{o=分配(o);//A.映射(x=>o[x]=f(o[x]))(键(o));//B返回o;};//模拟数据常量o={“a”:1,“b”:2,“c”:3};//并运行控制台日志(omap(sqr)(o));控制台日志(omap(mul(10))(o));
在线A o被重新分配。由于Javascript通过共享传递引用值,因此生成了o的浅拷贝。我们现在能够在omap中突变o,而不在父作用域中突变o。在B行中,map的返回值被忽略,因为map执行了o的变异。由于此副作用仍在omap中,并且在父范围中不可见,因此完全可以接受。
这不是最快的解决方案,而是一个声明性和可重用的解决方案。以下是与单行相同的实现,简洁但可读性较差:
const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);
附录-为什么默认情况下对象不可迭代?
ES2015指定了迭代器和可迭代协议。但对象仍然不可迭代,因此不可映射。原因是数据和程序级别的混合。
写一个很简单:
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;
}
示例代码:
> 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 }
注意:这个版本还允许您(可选)为回调设置this上下文,就像Array方法一样。
EDIT-更改为删除Object.prototype的使用,以确保它不会与对象上任何现有的名为map的属性冲突。
ES6:
Object.prototype.map = function(mapFunc) {
return Object.keys(this).map((key, index) => mapFunc(key, this[key], index));
}
2015年:
Object.prototype.map = function (mapFunc) {
var _this = this;
return Object.keys(this).map(function (key, index) {
return mapFunc(key, _this[key], index);
});
};
节点中的测试:
> a = {foo: "bar"}
{ foo: 'bar' }
> a.map((k,v,i) => v)
[ 'bar' ]
TypeScript中的对象映射器
我喜欢像这样使用Object.fromEntries的示例,但它们仍然不太好用。使用Object.keys然后查找关键字的答案实际上是在进行可能不需要的多次查找。
我希望有一个Object.map函数,但我们可以创建自己的函数,并将其称为objectMap,同时可以修改键和值:
用法(JavaScript):
const myObject = { 'a': 1, 'b': 2, 'c': 3 };
// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }
// modify both key and value
obj = objectMap(myObject,
val => val * 2 + '',
key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }
代码(TypeScript):
interface Dictionary<T> {
[key: string]: T;
}
function objectMap<TValue, TResult>(
obj: Dictionary<TValue>,
valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
keySelector?: (key: string, obj: Dictionary<TValue>) => string,
ctx?: Dictionary<TValue>
) {
const ret = {} as Dictionary<TResult>;
for (const key of Object.keys(obj)) {
const retKey = keySelector
? keySelector.call(ctx || null, key, obj)
: key;
const retVal = valSelector.call(ctx || null, obj[key], obj);
ret[retKey] = retVal;
}
return ret;
}
如果您没有使用TypeScript,请在TypeScript Playground中复制上述代码以获取JavaScript代码。
此外,我在参数列表中将keySelector放在valSelector之后的原因是它是可选的。
*亚历山大·米尔斯的回答值得称赞。
您可以在数组上使用map方法和forEach,但如果您想在Object上使用它,则可以使用如下扭曲:
使用Javascript(ES6)
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
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}
或者也可以使用其他循环,例如$.each方法,如下所示:
$.each(ob,function (key, value) {
ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}