简体   繁体   English

map function 用于对象(而不是数组)

[英]map function for objects (instead of arrays)

I have an object:我有一个 object:

myObject = { 'a': 1, 'b': 2, 'c': 3 }

I am looking for a native method, similar to Array.prototype.map that 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 map function for objects? JavaScript 是否有这样的map function 对象? (I want this for Node.JS, so I don't care about cross-browser issues.) (我想要这个用于 Node.JS,所以我不关心跨浏览器问题。)

There is no native map to the Object object, but how about this:没有到Object对象的本地map ,但是如何:

 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.reduce reduces an array to a single value by somewhat merging the previous value with the current. Array.prototype.reduce通过将先前的值与当前值进行某种程度的合并,将数组缩减为单个值。 The chain is initialized by an empty object {} .该链由一个空对象{}初始化。 On every iteration a new key of myObject is added with twice the key as the value.在每次迭代中,都会添加一个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))

How about a one-liner in JS ES10 / ES2019 ? JS ES10 / ES2019 中的单行怎么

Making use of Object.entries() and Object.fromEntries() :利用Object.entries()Object.fromEntries()

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

The same thing written as a function:同样的事情写成一个函数:

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

This function uses recursion to square nested objects as well:此函数也使用递归来平方嵌套对象:

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

With ES7 / ES2016 you cant' use Objects.fromEntries , but you can achieve the same using Object.assign in combination with spread operators and computed key names syntax:ES7 / ES2016 中,您不能使用Objects.fromEntries ,但您可以使用Object.assign结合扩展运算符计算键名称语法来实现相同的效果:

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

ES6 / ES2015 Doesn't allow Object.entries , but you could use Object.keys instead: ES6 / ES2015不允许Object.entries ,但您可以使用Object.keys代替:

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

ES6 also introduced for...of loops, which allow a more imperative style: ES6 还引入了for...of循环,允许更命令式的风格:

let newObj = {}

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


array.reduce()数组.reduce()

Instead of Object.fromEntries and Object.assign you can also use reduce for this:除了Object.fromEntriesObject.assign您还可以使用reduce

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


Inherited properties and the prototype chain:继承属性和原型链:

In some rare situation you may need to map a class-like object which holds properties of an inherited object on its prototype-chain .在一些罕见的情况下,您可能需要映射一个对象,该对象在其原型链上保存继承对象的属性。 In such cases Object.keys() and Object.entries() won't work, because these functions do not include the prototype chain.在这种情况下Object.keys()Object.entries()将不起作用,因为这些函数不包括原型链。

If you need to map inherited properties, you can use for (key in myObj) {...} .如果需要映射继承的属性,可以使用for (key in myObj) {...}

Here is an example of such situation:下面是这种情况的一个例子:

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'
}

However, please do me a favor and avoid inheritance .但是,请帮我一个忙,避免继承 :-) :-)

No native methods, but lodash#mapValues will do the job brilliantly没有本地方法,但lodash#mapValues可以出色地完成这项工作

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }

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 this context for the callback, just like the Array method.注意:此版本还允许您(可选)为回调设置this上下文,就像Array方法一样。

EDIT - changed to remove use of Object.prototype , to ensure that it doesn't clash with any existing property named map on the object.编辑- 更改为删除Object.prototype使用,以确保它不会与对象上任何名为map现有属性发生冲突。

You could use Object.keys and then forEach over 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.keys returns an array containing only the object's own enumerable properties, thus it behaves like a for..in loop with a hasOwnProperty check.请注意, Object.keys返回一个仅包含对象自己的可枚举属性的数组,因此它的行为类似于带有hasOwnProperty检查的for..in循环。

This is really annoying, and everyone in the JS community knows it.这真的很烦人,JS社区的每个人都知道。 There should be this functionality:应该有这个功能:

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

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

JavaScript just got the new Object.fromEntries method. 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.上面的代码将对象转换为嵌套数组( [[<key>,<value>], ...] ),您可以映射到其中。 Object.fromEntries converts the Array back to an Object. Object.fromEntries将 Array 转换回 Object。

The cool thing about this pattern, is that you can now easily take object keys into account while mapping.这种模式很酷的一点是,您现在可以在映射时轻松考虑对象键。

Documentation文档

Browser Support浏览器支持

Object.fromEntries is currently only supported by these browsers/engines , nevertheless there are polyfills available (eg @babel/polyfill ). Object.fromEntries目前只被这些浏览器/引擎支持,不过还是有 polyfills 可用(例如@babel/polyfill )。

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.它误用了Array.prototype.reduce ,因为减少意味着改变复合类型的结构,这在这种情况下不会发生。
  • It is not particularly reusable它不是特别可重用

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 o is reassigned.在行 A o被重新分配。 Since Javascript passes reference values by sharing , a shallow copy of o is generated.由于 Javascript 通过 shared 传递引用值因此生成了o的浅拷贝。 We are now able to mutate o within omap without mutating o in the parent scope.我们现在可以在omap改变o而不改变父作用域中的o
  • In line B map 's return value is ignored, because map performs a mutation of o .在 B 行中, map的返回值被忽略,因为map执行了o的突变。 Since this side effect remains within omap and isn't visible in the parent scope, it is totally acceptable.由于此副作用保留在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. ES2015 指定了迭代器和可迭代协议。 But objects are still not iterable and thus not mappable.但是对象仍然不可迭代,因此不可映射。 The reason is the mixing of data and program level .原因是数据和程序级别的混合

Minimal version最小版本

ES2017 ES2017

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
                                                  ↑↑↑↑↑

ES2019 ES2019

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

For maximum performance.以获得最佳性能。

If your object doesn't change often but needs to be iterated on often I suggest using a native Map as a cache.如果您的对象不经常更改但需要经常迭代,我建议使用本机 Map 作为缓存。

 // 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 already works in Chrome, Edge, Firefox and beta Opera so it's a future-proof feature. Object.entries 已经在 Chrome、Edge、Firefox 和 beta Opera 中运行,因此它是一个面向未来的功能。 It's from ES7 so polyfill it https://github.com/es-shims/Object.entries for IE where it doesn't work.它来自 ES7,因此为 IE 对其进行 polyfill https://github.com/es-shims/Object.entries在它不起作用的情况下。

you can use map method and forEach on arrays but if you want to use it on Object then you can use it with twist like below:你可以在数组上使用map方法和forEach但如果你想在Object上使用它,那么你可以像下面一样使用它:

Using Javascript (ES6)使用 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}

Using jQuery使用 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}

Or you can use other loops also like $.each method as below example:或者您也可以使用其他循环,例如$.each方法,如下例所示:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}

You can convert an object to array simply by using the following:您只需使用以下命令即可将对象转换为数组:

You can convert the object values to an array:您可以将对象值转换为数组:

 myObject = { 'a': 1, 'b': 2, 'c': 3 }; let valuesArray = Object.values(myObject); console.log(valuesArray);

You can convert the object keys to an array:您可以将对象键转换为数组:

 myObject = { 'a': 1, 'b': 2, 'c': 3 }; let keysArray = Object.keys(myObject); console.log(keysArray);

Now you can perform normal array operations, including the 'map' function现在您可以执行普通的数组操作,包括“map”函数

The map function does not exist on the Object.prototype however you can emulate it like so Object.prototype上不存在map function ,但是您可以像这样模拟它

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

EDIT: The canonical way using newer JavaScript features is -编辑:使用较新的 JavaScript 功能的规范方式是 -

const identity = x =>
  x

const omap = (f = identity, o = {}) =>
  Object.fromEntries(
    Object.entries(o).map(([ k, v ]) =>
      [ k, f(v) ]
    )
  )

Where o is some object and f is your mapping function.其中o是某个对象, f是您的映射函数。 Or we could say, given a function from a -> b , and an object with values of type a , produce an object with values of type b .或者我们可以说,给定一个来自a -> b的函数,以及一个值类型为a的对象,生成一个值类型为b的对象。 As a pseudo type signature -作为伪类型签名 -

// omap : (a -> b, { a }) -> { b }

The original answer was written to demonstrate a powerful combinator, mapReduce which allows us to think of our transformation in a different way最初的答案是为了展示一个强大的组合器mapReduce ,它允许我们以不同的方式思考我们的转换

  1. m , the mapping function – gives you a chance to transform the incoming element before… m映射函数——让你有机会在......
  2. r , the reducing function – this function combines the accumulator with the result of the mapped element rreduce函数——这个函数将累加器与映射元素的结果结合起来

Intuitively, mapReduce creates a new reducer we can plug directly into Array.prototype.reduce .直观地说, mapReduce创建了一个新的 reducer,我们可以直接插入到Array.prototype.reduce But more importantly, we can implement our object functor implementation omap plainly by utilizing the object monoid, Object.assign and {} .但更重要的是,我们可以通过使用对象 monoid、 Object.assign{}来简单地实现我们的对象函子实现omap

 const identity = x => x const mapReduce = (m, r) => (a, x) => r (a, m (x)) const omap = (f = identity, o = {}) => Object .keys (o) .reduce ( mapReduce ( k => ({ [k]: f (o[k]) }) , Object.assign ) , {} ) const square = x => x * x const data = { a : 1, b : 2, c : 3 } console .log (omap (square, data)) // { a : 1, b : 4, c : 9 }

Notice the only part of the program we actually had to write is the mapping implementation itself –请注意,我们实际上必须编写的程序的唯一部分是映射实现本身——

k => ({ [k]: f (o[k]) })

Which says, given a known object o and some key k , construct an object and whose computed property k is the result of calling f on the key's value, o[k] .也就是说,给定一个已知对象o和一些键k ,构造一个对象,其计算属性k是对键的值o[k]调用f的结果。

We get a glimpse of mapReduce 's sequencing potential if we first abstract oreduce我们得到的一瞥mapReduce的测序的潜力,如果我们第一抽象oreduce

// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => [ k, o[k] ]
          , f
          )
      , r
      )

// omap : (a -> b, {a}) -> {b}
const omap = (f = identity, o = {}) =>
  oreduce
    ( mapReduce
        ( ([ k, v ]) =>
            ({ [k]: f (v) })
        , Object.assign
        )
    , {}
    , o
    )

Everything works the same, but omap can be defined at a higher-level now.一切都一样,但omap可以在更高级别定义omap Of course the new Object.entries makes this look silly, but the exercise is still important to the learner.当然,新的Object.entries使这看起来很傻,但这个练习对学习者来说仍然很重要。

You won't see the full potential of mapReduce here, but I share this answer because it's interesting to see just how many places it can be applied.您不会在这里看到mapReduce的全部潜力,但我分享这个答案,因为看看它可以应用到多少地方很有趣。 If you're interested in how it is derived and other ways it could be useful, please see this answer .如果您对它的派生方式以及它可能有用的其他方式感兴趣,请参阅此答案

I came upon this as a first-item in a Google search trying to learn to do this, and thought I would share for other folsk finding this recently the solution I found, which uses the npm package immutable.我在尝试学习这样做的 Google 搜索中将此作为第一项,并认为我会与其他人分享最近发现此问题的解决方案,该解决方案使用 npm 包不可变。

I think its interesting to share because immutable uses the OP's EXACT situation in their own documentation - the following is not my own code but pulled from the current immutable-js documentation:我认为分享很有趣,因为 immutable 在他们自己的文档中使用了 OP 的 EXACT 情况——以下不是我自己的代码,而是从当前的 immutable-js 文档中提取的:

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 } 

Not that Seq has other properties ("Seq describes a lazy operation, allowing them to efficiently chain use of all the higher-order collection methods (such as map and filter) by not creating intermediate collections") and that some other immutable-js data structures might also do the job quite efficiently.并不是说 Seq 具有其他属性(“Seq 描述了一个惰性操作,允许它们通过不创建中间集合来有效地链接所有高阶集合方法(例如 map 和 filter)”)以及一些其他不可变的 js 数据结构也可能非常有效地完成这项工作。

Anyone using this method will of course have to npm install immutable and might want to read the docs:使用此方法的任何人当然都必须npm install immutable并且可能想要阅读文档:

https://facebook.github.io/immutable-js/ https://facebook.github.io/immutable-js/

Based on @Amberlamps answer, here's a utility function (as a comment it looked ugly)基于@Amberlamps 的回答,这是一个实用函数(作为评论它看起来很难看)

function mapObject(obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, value) {
        newObj[value] = mapFunc(obj[value]);
        return newObj;
    }, {});
}

and the use is:用途是:

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}

The first function answers the question. 第一个功能回答了这个问题。 It creates a map. 它创建一个地图。

The second function doesn't create a map. 第二个功能不会创建地图。 Instead, it loops through properties in the existing object using hasOwnProperty() . 而是使用hasOwnProperty()遍历现有对象中的属性。 This map alternative may be a better solution in some situations. 在某些情况下,这种地图替代方法可能是更好的解决方案。

 var myObject = { 'a': 1, 'b': 2, 'c': 3 } //creates a map (answers question, but function below is //much 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 } //Doesn't create a map, just applies the function to //a specific property using existing data function getValue(key) { for (var k in myObject) { if (myObject.hasOwnProperty(key)) { var value = myObject[key] return value * value; //stops iteration } } } 
 Input: <input id="input" value="" placeholder="a, b or c"><br> Output:<input id="output"><br> <button onclick="output.value=getValue(input.value)" >Get value</button> 

My response is largely based off the highest rated response here and hopefully everyone understands (have the same explanation on my GitHub, too).我的回答主要基于这里评分最高的回答,希望每个人都能理解(在我的 GitHub 上也有相同的解释)。 This is why his impementation with map works:这就是为什么他对地图的执行有效的原因:

Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +    
')');

The purpose of the function is to take an object and modify the original contents of the object using a method available to all objects (objects and arrays alike) without returning an array.该函数的目的是获取一个对象并使用所有对象(对象和数组等)都可用的方法修改该对象的原始内容,而不返回数组。 Almost everything within JS is an object, and for that reason elements further down the pipeline of inheritance can potentially technically use those available to those up the line (and the reverse it appears). JS 中的几乎所有东西都是一个对象,因此,继承管道下游的元素在技术上可能会使用那些在线可用的元素(反之亦然)。

The reason that this works is due to the .map functions returning an array REQUIRING that you provide an explicit or implicit RETURN of an array instead of simply modifying an existing object.之所以有效,是因为 .map 函数返回一个数组,要求您提供数组的显式或隐式 RETURN,而不是简单地修改现有对象。 You essentially trick the program into thinking the object is an array by using Object.keys which will allow you to use the map function with its acting on the values the individual keys are associated with (I actually accidentally returned arrays but fixed it).你基本上通过使用 Object.keys 来欺骗程序认为对象是一个数组,这将允许你使用 map 函数来处理与各个键关联的值(我实际上不小心返回了数组但修复了它)。 As long as there isn't a return in the normal sense, there will be no array created with the original object stil intact and modified as programmed.只要没有正常意义上的返回,就不会创建数组,原始对象仍然完好无损并按程序修改。

This particular program takes an object called images and takes the values of its keys and appends url tags for use within another function.这个特定的程序接受一个名为图像的对象,并获取其键的值并附加 url 标签以供在另一个函数中使用。 Original is this:原文是这样的:

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

...and modified is this: ...并修改为:

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

The object's original structure is left intact allowing for normal property access as long as there isn't a return.对象的原始结构保持不变,只要没有返回就可以正常访问属性。 Do NOT have it return an array like normal and everything will be fine.不要让它像平常一样返回一个数组,一切都会好起来的。 The goal is REASSIGNING the original values (images[key]) to what is wanted and not anything else.目标是将原始值 (images[key]) 重新分配给所需的值,而不是其他任何值。 As far as I know, in order to prevent array output there HAS to be REASSIGNMENT of images[key] and no implicit or explicit request to return an array (variable assignment does this and was glitching back and forth for me).据我所知,为了防止数组输出,必须对图像 [key] 进行重新分配,并且没有隐式或显式请求返回数组(变量赋值执行此操作并且对我来说来回出现故障)。

EDIT:编辑:

Going to address his other method regarding new object creation to avoid modifying original object (and reassignment appears to still be necessary in order to avoid accidentally creating an array as output).将解决他关于创建新对象的另一种方法以避免修改原始对象(并且重新分配似乎仍然是必要的,以避免意外地创建一个数组作为输出)。 These functions use arrow syntax and are if you simply want to create a new object for future use.这些函数使用箭头语法,如果您只是想创建一个新对象以供将来使用。

const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
                result[key] = mapFn(obj)[key];
                return result;
            }, {});

var newImages = mapper(images, (value) => value);

The way these functions work is like so:这些函数的工作方式是这样的:

mapFn takes the function to be added later (in this case (value) => value) and simply returns whatever is stored there as a value for that key (or multiplied by two if you change the return value like he did) in mapFn(obj)[key], mapFn 接受稍后要添加的函数(在本例中为 (value) => value),并在 mapFn(对象)[键],

and then redefines the original value associated with the key in result[key] = mapFn(obj)[key]然后在 result[key] = mapFn(obj)[key] 中重新定义与键关联的原始值

and returns the operation performed on result (the accumulator located in the brackets initiated at the end of the .reduce function).并返回对结果执行的操作(位于 .reduce 函数末尾启动的括号中的累加器)。

All of this is being performed on the chosen object and STILL there CANNOT be an implicit request for a returned array and only works when reassigning values as far as I can tell.所有这些都在选定的对象上执行,并且仍然不能对返回的数组进行隐式请求,并且据我所知仅在重新分配值时才有效。 This requires some mental gymnastics but reduces the lines of code needed as can be seen above.这需要一些心理体操,但减少了所需的代码行数,如上所示。 Output is exactly the same as can be seen below:输出完全相同,如下所示:

{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"}

Keep in mind this worked with NON-NUMBERS.请记住,这适用于非数字。 You CAN duplicate ANY object by SIMPLY RETURNING THE VALUE in the mapFN function.您可以通过在 mapFN 函数中简单返回值来复制任何对象。

Object Mapper in TypeScript TypeScript 中的对象映射器

I like the examples that use Object.fromEntries such as this one , but still, they are not very easy to use.我喜欢使用Object.fromEntries的示例,例如this one ,但它们仍然不是很容易使用。 The answers that use Object.keys and then look up the key are actually doing multiple look-ups that may not be necessary.使用Object.keys然后查找key的答案实际上是在进行多次查找,这可能不是必需的。

I wished there was an Object.map function, but we can create our own and call it objectMap with the ability to modify both key and value :我希望有一个Object.map函数,但我们可以创建自己的函数并将其称为objectMap ,并具有修改keyvalue的能力:

Usage (JavaScript):用法(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' }

Code (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;
}

If you are not using TypeScript then copy the above code in TypeScript Playground to get the JavaScript code.如果您不使用 TypeScript,请在TypeScript Playground 中复制上述代码以获取 JavaScript 代码。

Also, the reason I put keySelector after valSelector in the parameter list, is because it is optional.另外,我在参数列表valSelector放在keySelector之后的原因是因为它是可选的。

* Some credit go to alexander-mills' answer . * 部分功劳归于 alexander-mills 的回答

If you're interested in map ping not only values but also keys, I have written Object.map(valueMapper, keyMapper) , that behaves this way:如果您不仅对map值而且对map ping 感兴趣,我已经编写了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 }

I needed a version that allowed modifying the keys as well (based on @Amberlamps and @yonatanmn answers);我需要一个允许修改键的版本(基于@Amberlamps 和@yonatanmn 的答案);

var facts = [ // can be an object or array - see jsfiddle below
    {uuid:"asdfasdf",color:"red"},
    {uuid:"sdfgsdfg",color:"green"},
    {uuid:"dfghdfgh",color:"blue"}
];

var factObject = mapObject({}, facts, function(key, item) {
    return [item.uuid, {test:item.color, oldKey:key}];
});

function mapObject(empty, obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, key) {
        var kvPair = mapFunc(key, obj[key]);
        newObj[kvPair[0]] = kvPair[1];
        return newObj;
    }, empty);
}

factObject=事实对象=

{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}

Edit: slight change to pass in the starting object {}.编辑:稍微更改以传入起始对象 {}。 Allows it to be [] (if the keys are integers)允许它是 [] (如果键是整数)

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 }

To responds more closely to what precisely the OP asked for, the OP wants an object:为了更准确地响应 OP 的要求,OP 需要一个对象:

myObject = { 'a': 1, 'b': 2, 'c': 3 }

to have a map method myObject.map ,有一个地图方法myObject.map

similar to Array.prototype.map that would be used as follows:类似于 Array.prototype.map 的用法如下:

 newObject = myObject.map(function (value, label) { newObject = myObject.map(function (value, label) {\n    return value * value;返回值 * 值;\n}); });\n// newObject is now { 'a': 1, 'b': 4, 'c': 9 } // newObject 现在是 { 'a': 1, 'b': 4, 'c': 9 }

The imho best (measured in terms to " close to what is asked " + "no ES{5,6,7} required needlessly") answer would be:恕我直言(以“接近所问的内容”+“不必要地不需要 ES{5,6,7}”衡量)答案是:

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

The code above avoids intentionally using any language features, only available in recent ECMAScript editions.上面的代码故意避免使用任何语言特性,仅在最近的 ECMAScript 版本中可用。 With the code above the problem can be solved lke this:使用上面的代码,问题可以这样解决:

 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);
alternative test code here 替代测试代码在这里

Besides frowned upon by some, it would be a possibility to insert the solution in the prototype chain like this.除了有些人不屑一顾之外,像这样将解决方案插入原型链中也是一种可能。

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

Something, which when done with careful oversight should not have any ill effects and not impact map method of other objects (ie Array's map ).一些东西,在仔细监督下完成时不应该有任何不良影响,也不会影响其他对象的map方法(即 Array 的map )。

First, convert your HTMLCollection using Object.entries(collection).首先,使用 Object.entries(collection) 转换您的 HTMLCollection。 Then it's an iterable you can now use the .map method on it.然后它是一个可迭代的,您现在可以在其上使用 .map 方法。

Object.entries(collection).map(...)

reference https://medium.com/@js_tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31参考https://medium.com/@js_tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31

Define a function mapEntries.定义一个函数mapEntries。

mapEntries takes a callback function that is called on each entry in the object with the parameters value, key and object. mapEntries 接受一个回调函数,该函数在对象中的每个条目上使用参数 value、key 和 object 调用。 It should return the a new value.它应该返回一个新值。

mapEntries should return a new object with the new values returned from the callback. mapEntries 应该返回一个新对象,其中包含从回调返回的新值。

Object.defineProperty(Object.prototype, 'mapEntries', {
  enumerable: false,
  value: function (mapEntriesCallback) {
    return Object.fromEntries(
      Object.entries(this).map(
        ([key, value]) => [key, mapEntriesCallback(value, key, this)]
      )
    )
  }
})


// Usage example:

var object = {a: 1, b: 2, c: 3}
var newObject = object.mapEntries(value => value * value)
console.log(newObject)
//> {a: 1, b: 4, c: 9}

Edit: A previous version didn't specify that this is not an enumerable property编辑:以前的版本没有指定这不是一个可枚举的属性

const mapObject = (targetObject, callbackFn) => {
    if (!targetObject) return targetObject;
    if (Array.isArray(targetObject)){
        return targetObject.map((v)=>mapObject(v, callbackFn))
    }
    return Object.entries(targetObject).reduce((acc,[key, value]) => {
        const res = callbackFn(key, value);
        if (!Array.isArray(res) && typeof res ==='object'){
            return {...acc, [key]: mapObject(res, callbackFn)}
        }
        if (Array.isArray(res)){
            return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
        }
        return {...acc, [key]: res};
    },{})
};
const mapped = mapObject(a,(key,value)=> {
    if (!Array.isArray(value) && key === 'a') return ;
    if (!Array.isArray(value) && key === 'e') return [];
    if (!Array.isArray(value) && key === 'g') return value * value;
    return value;
});
console.log(JSON.stringify(mapped)); 
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}

This function goes recursively through the object and arrays of objects.这个函数递归地遍历对象和对象数组。 Attributes can be deleted if returned undefined如果返回未定义,则可以删除属性

Hey wrote a little mapper function that might help.嘿写了一个可能有帮助的小映射器函数。

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

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:这对我有用:

var mapped = [item].map(myMapFunction).pop();

If anyone was looking for a simple solution that maps an object to a new object or to an array:如果有人正在寻找将对象映射到新对象或数组的简单解决方案:

// 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]))
);

This may not work for all objects or all mapping functions, but it works for plain shallow objects and straightforward mapping functions which is all I needed.这可能不适用于所有对象或所有映射函数,但它适用于我所需要的普通浅层对象和直接映射函数。

A different take on it is to use a custom json stringify function that can also work on deep objects.另一种做法是使用自定义的 json stringify 函数,该函数也可以处理深层对象。 This might be useful if you intend to post it to the server anyway as json如果您打算将其作为 json 发布到服务器,这可能很有用

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

我只处理字符串以减少豁免:

Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);

I needed a function to optionally map not only (nor exclusively) values, but also keys.我需要一个函数来选择性地映射不仅(也不是唯一的)值,而且还映射键。 The original object should not change.原始对象不应更改。 The object contained only primitive values also.该对象也仅包含原始值。

function mappedObject(obj, keyMapper, valueMapper) {

    const mapped = {};
    const keys   = Object.keys(obj);
    const mapKey = typeof keyMapper == 'function';
    const mapVal = typeof valueMapper == 'function';

    for (let i = 0; i < keys.length; i++) {
        const key = mapKey ? keyMapper(keys[i]) : keys[i];
        const val = mapVal ? valueMapper(obj[keys[i]]) : obj[keys[i]];
        mapped[key] = val;
    }

    return mapped;
}

Use.利用。 Pass a keymapper and a valuemapper function:传递一个 keymapper 和一个 valuemapper 函数:

const o1 = { x: 1, c: 2 }
mappedObject(o1, k => k + '0', v => v + 1) // {x0: 2, c0: 3}

 const orig = { 'a': 1, 'b': 2, 'c': 3 } const result = _.transform(orig, (r, v, k) => r[k.trim()] = v * 2); console.log(result); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> 

Use new _.transform() to transforms object . 使用new _.transform()转换object

 var myObject = { 'a': 1, 'b': 2, 'c': 3 }; Object.keys(myObject).filter((item) => myObject[item] *= 2) console.log(myObject)

 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 }

Here is another version that allows the mapping function to declare any number of new properties (keys and values) based on the current key and value.这是另一个版本,它允许映射函数基于当前键和值声明任意数量的新属性(键和值)。 E: Now works with arrays too. E:现在也适用于数组。

 Object.defineProperty(Object.prototype, 'mapEntries', { value: function(f,a=Array.isArray(this)?[]:{}) { return Object.entries(this).reduce( (o, [k,v]) => Object.assign(o, f(v, Array.isArray(a)?Number(k):k, this)), a); } }); const data = { a : 1, b : 2, c : 3 }; const calculate = (v, k) => ({ [k+'_square']: v*v, [k+'_cube']: v*v*v }); console.log( data.mapEntries( calculate ) ); // { // "a_square": 1, "a_cube": 1, // "b_square": 4, "b_cube": 8, // "c_square": 9, "c_cube": 27 // } // Demonstration with an array: const arr = [ 'a', 'b', 'c' ]; const duplicate = (v, i) => ({ [i*2]: v, [i*2+1]: v+v }); console.log( arr.mapEntries( duplicate ) ); // [ "a", "aa", "b", "bb", "c", "cc" ]

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)

ES6: ES6:

Object.prototype.map = function(mapFunc) {
    return Object.keys(this).map((key, index) => mapFunc(key, this[key], index));
}

ES2015: ES2015:

Object.prototype.map = function (mapFunc) {
    var _this = this;

    return Object.keys(this).map(function (key, index) {
        return mapFunc(key, _this[key], index);
    });
};

test in node:在节点中测试:

> a = {foo: "bar"}
{ foo: 'bar' }
> a.map((k,v,i) => v)
[ 'bar' ]

Use following map function to define myObject.map使用以下 map 函数定义myObject.map

o => f=> Object.keys(o).reduce((a,c)=> c=='map' ? a : (a[c]=f(o[c],c),a), {})

 let map = o => f=> Object.keys(o).reduce((a,c)=> c=='map' ? a : (a[c]=f(o[c],c),a), {}) // TEST init myObject = { 'a': 1, 'b': 2, 'c': 3 } myObject.map = map(myObject); // you can do this instead above line but it is not recommended // ( you will see `map` key any/all objects) // Object.prototype.map = map(myObject); // OP desired interface described in question newObject = myObject.map(function (value, label) { return value * value; }); console.log(newObject);

2020 updates, overwriting Object.prototype 2020 更新,覆盖 Object.prototype

Object.prototype.map = function(func){
    for(const [k, v] of Object.entries(this)){
        func(k, v);
    }
    return this;
}

Object.prototype.reject = function(func){
    for(const [k, v] of Object.entries(this)){
        if(func(k, v)){
            delete this[k];
        }
    }
    return this;
}

Object.prototype.filter = function(func){
    let res = {};
    for(const [k, v] of Object.entries(this)){
        if(!func(k, v)){
            delete this[k];
        }
    }
    return this;
}

a = {
    1: ['a', '1'],
    2: ['b', '2'],
    3: ['a', '3'],
};

console.log(Object.assign({}, a).map((k, v) => v.push('nice')));
// {
//   '1': [ 'a', '1', 'nice' ],
//   '2': [ 'b', '2', 'nice' ],
//   '3': [ 'a', '3', 'nice' ]
// }

console.log(Object.assign({}, a).reject((k, v)=>v[0] === 'a'));
// { '2': [ 'b', '2', 'nice' ] }

console.log(Object.assign({}, a).filter((k, v)=>v[0] === 'a'));
// { '1': [ 'a', '1', 'nice' ], '3': [ 'a', '3', 'nice' ] }

Async, anyone?异步,有人吗?

Despite of the large number of comments, I didn't find a solution for using an async mapper.尽管有大量评论,但我没有找到使用async映射器的解决方案。 Here's mine.这是我的。

Uses p-map , a trusted (@sindresorhus) and small dependency.使用p-map ,一个受信任的 (@sindresorhus) 和小的依赖项。

(note that no options are passed to p-map . Refer to the docs if you need to adjust concurrency/error handling). (请注意,没有选项传递给p-map 。如果您需要调整并发/错误处理,请参阅文档)。

Typescript:打字稿:

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

Plain JS:普通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);
};

};

Usage example:用法示例:

(highly contrived, no error handling, no types) (高度人为的,没有错误处理,没有类型)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM