我有一个目标:

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使用这个,所以我不关心跨浏览器问题。)


当前回答

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 }

其他回答

公认的答案有两个缺点:

它误用了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(valueMapper,keyMapper),其行为如下:

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 }

JS ES10/ES2019中的一行怎么样?

使用Object.entries()和Object.fromEntries():

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

同样的东西写为函数:

function objMap(obj, func) {
  return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, func(v)]));
}

// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);

此函数还使用递归对嵌套对象进行平方:

function objMap(obj, func) {
  return Object.fromEntries(
    Object.entries(obj).map(([k, v]) => 
      [k, v === Object(v) ? objMap(v, func) : func(v)]
    )
  );
}

// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);

对于ES7/ES2016,您不能使用Objects.fromEntries,但可以使用Object.assign结合排列运算符和计算的关键字名称语法实现相同的功能:

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

ES6/ES2015不允许Object.entries,但可以改用Object.keys:

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

ES6还为。。。循环,允许更命令式的样式:

let newObj = {}

for (let [k, v] of Object.entries(obj)) {
  newObj[k] = v * v;
}


array.reduce()

您也可以使用reduce代替Object.fromEntries和Object.assign:

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


继承的财产和原型链:

在一些罕见的情况下,您可能需要映射一个类类对象,该类对象在其原型链上保存继承对象的财产。在这种情况下,Object.keys()和Object.entries()将无法工作,因为这些函数不包括原型链。

如果需要映射继承的财产,可以使用for(myObj中的键){…}。

以下是此类情况的示例:

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.
console.log(Object.entries(obj2));  // Prints: an empty Array.

for (let key in obj2) {
  console.log(key);              // Prints: 'a', 'b', 'c'
}

不过,请帮我一个忙,避免继承

映射函数在Object.prototype上不存在,但是您可以这样模拟它

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

Async,有人吗?

尽管有大量的评论,但我没有找到使用异步映射器的解决方案。这是我的。

使用p-map,一个受信任的(@sindresorhus)和小的依赖关系。

(注意,没有选项传递给p-map。如果需要调整并发/错误处理,请参阅文档)。

字体:

import pMap from "p-map";

export const objectMapAsync = async <InputType, ResultType>(
  object: { [s: string]: InputType } | ArrayLike<InputType>,
  mapper: (input: InputType, key: string, index: number) => Promise<ResultType>
): Promise<{
  [k: string]: ResultType;
}> => {
  const mappedTuples = await pMap(
    Object.entries(object),
    async ([key, value], index) => {
      const result = await mapper(value, key, index);
      return [key, result];
    }
  );

  return Object.fromEntries(mappedTuples);
};

普通JS:

import pMap from "p-map";

export const objectMapAsync = async (
  object,
  mapper
) => {
  const mappedTuples = await pMap(
    Object.entries(object),
    async ([key, value], index) => {
      const result = await mapper(value, key, index);
      return [key, result];
    }
  );

  return Object.fromEntries(mappedTuples);
};

};

用法示例:

(精心设计,无错误处理,无类型)

// Our object in question.
const ourFavouriteCharacters = {
  me: "luke",
  you: "vader",
  everyone: "chewbacca",
};

// An async function operating on the object's values (in this case, strings)
const fetchCharacter = (charName) =>
  fetch(`https://swapi.dev/api/people?search=${charName}`)
    .then((res) => res.json())
    .then((res) => res.results[0]);

// `objectMapAsync` will return the final mapped object to us
//  (wrapped in a Promise)
objectMapAsync(ourFavouriteCharacters, fetchCharacter).then((res) =>
  console.log(res)
);