首页 文章

对象的映射函数(而不是数组)

提问于
浏览
641

我有一个对象:

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是否具有对象的 map 函数? (我想将它用于Node.JS,所以我不关心跨浏览器问题 . )

25 回答

  • 955

    嘿写了一个可能有帮助的小映射器功能 .

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

    您可以在返回的键数组上使用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;
    });
    

    或者以更模块化的方式:

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

    请注意, Object.keys 返回一个仅包含对象自己的可枚举属性的数组,因此它的行为类似于带有 hasOwnProperty 检查的 for..in 循环 .

  • -1

    这是直接的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}
    

    这是天真的实现:

    Object.map = function(obj, fn, ctx){
    
        const ret = {};
    
        Object.keys(obj).forEach(function(k){
            ret[k] = fn.call(ctx || null, k, obj[k]);
        });
    
        return ret;
    };
    

    必须始终自己实现这一点非常烦人;)

    如果你想要一些更复杂的东西,这不会干扰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;
    });
    

    但是将这个map函数添加到Object是安全的,只是不要添加到Object.prototype .

    Object.map = ... // fairly safe
    Object.prototype.map ... // not ok
    
  • 1

    基于@Amberlamps答案,这里是一个实用功能(作为评论看起来很难看)

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

    用途是:

    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}
    
  • 1

    另一种观点是使用自定义json stringify函数,该函数也可以在深层对象上运行 . 如果您打算将其作为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))
    
  • 3

    我来到这里寻找并回答将对象映射到数组并获得此页面的结果 . 如果你来到这里寻找我的答案,这里是你如何映射和对象的数组 .

    您可以使用map从对象返回一个新数组,如下所示:

    var newObject = Object.keys(myObject).map(function(key) {
       return myObject[key];
    });
    
  • 14

    这个答案已被重写,以展示一个强大的组合, mapReduce ,它允许我们以不同的方式思考我们的转变

    • m ,映射函数 - 让您有机会在传入元素之前转换...

    • r ,reduce函数 - 此函数将累加器与映射元素的结果组合在一起

    直观地说, mapReduce 创建了一个新的减速器,我们可以直接插入 Array.prototype.reduce . 但更重要的是,我们可以通过利用对象monoid, Object.assign{} 来实现我们的对象仿函数实现 omap .

    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 }
    

    请注意,我们实际必须编写的程序的唯一部分是映射实现本身 -

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

    其中,给定一个已知对象 o 和一些键 k ,构造一个对象,其属性 k 是在键的值 o [k] 上调用 f 的结果 .

    如果我们第一次抽象 oreduce ,我们可以看到 mapReduce 的测序潜力

    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
                          )
              , {}
              )
    

    一切都是一样的,但现在可以在更高级别定义 omap . 当然新的 Object.entries 让这看起来很傻,但练习对学习者来说仍然很重要 .

    你不会在这里看到 mapReduce 的全部潜力,但我分享了这个答案,因为's interesting to see just how many places it can be applied. If you'对它的衍生方式以及它可能有用的其他方式感兴趣,请参阅this answer .

  • 94

    如果有人在寻找将对象映射到新对象或数组的简单解决方案:

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

    这可能不适用于所有对象或所有映射函数,但它适用于简单的浅层对象和直接的映射函数,这是我所需要的 .

  • 20

    最小版本(es6):

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

    如果你对 map 不仅对值而且对键都感兴趣,我写了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 }
    
  • 167

    我特别想使用我用于单个对象的数组的相同功能,并希望保持简单 . 这对我有用:

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

    为了获得最佳性能

    如果您的对象不经常更改但需要经常迭代,我建议使用本机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已经可以在Chrome,Edge,Firefox和beta Opera中使用,所以它来自ES7,因此它可以填充它https://github.com/es-shims/Object.entries,因为IE无效 .

  • 2

    您可以在数组上使用 map 方法和 forEach ,但如果您想在 Object 上使用它,那么您可以像下面一样使用它:

    Using 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

    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}
    
  • 1

    我发现这是谷歌搜索中的第一项,试图学习如何做到这一点,并认为我会分享其他folsk最近发现我找到的解决方案,它使用npm包不可变 .

    我觉得分享很有意思,因为不可变在他们自己的文档中使用了OP的精确情况 - 以下不是我自己的代码,而是从当前的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 }
    

    并不是说Seq有其他属性(“Seq描述了一个惰性操作,允许它们有效地链接使用所有高阶集合方法(例如map和通过不创建中间集合“)和其他一些不可变的js数据结构也可以非常有效地完成工作 .

    使用此方法的任何人当然都必须 npm install immutable 并且可能想要阅读文档:

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

  • 12

    接受的答案有两个缺点:

    • 它误用 Array.prototype.reduce ,因为减少意味着改变复合类型的结构,在这种情况下不会发生 .

    • 它不是特别可重复使用的


    ES6 / ES2015功能方法

    请注意,所有功能都以咖喱形式定义 .

    // 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传递参考值by sharing,因此会生成 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指定了迭代器和可迭代协议 . 但是对象仍然不可迭代,因此无法映射 . The reason is the mixing of data and program level .

  • -1

    没有本地方法,但lodash#mapValues将完美地完成这项工作

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

    如何在普通JS( ES6 / ES2015 )中使用立即变量赋值的单行程怎么样?

    使用spread operatorcomputed key name语法:

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

    jsbin

    使用reduce的另一个版本:

    let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});
    

    jsbin

    作为函数的第一个例子:

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

    jsbin

    如果要以函数样式映射嵌套对象 recursively ,可以这样做:

    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.
        ,{})
    

    jsbin

    或者更重要的是,像这样:

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

    jsbin

    ES7 / ES2016 起,您可以使用Object.entries而不是 Object.keys ,例如像这样:

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

    继承属性和原型链:

    在极少数情况下,您可能需要映射类似于对象的对象,该对象在其prototype-chain上包含继承对象的属性 . 在这种情况下 Object.keys() 将无法工作,因为 Object.keys() 不会枚举继承的属性 . 如果需要映射继承的属性,则应使用 for (key in myObj) {...} .

    下面是一个继承另一个对象属性的对象示例,以及 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'
    }
    

    jsbin

    但是,请帮我一个忙,避免继承 . :-)

  • 1

    map function 上不存在 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;
    });
    
  • 5
    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 }
    
  • 55

    您可以通过有序对使用简单的for-in循环 . 我使用了 hasOwnProperty() ,因为您为对象创建了三个属性(带有值) . 第一种方法不会创建 Map . 相反,它只是将函数应用于单个元素,这可以在很多情况下大大加快执行速度 .

    第二种方法以与第一种方法类似的方式创建 Map ,但可能比这里的其他答案慢得多 .

    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
    
    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>
    
  • 2

    Object 对象没有原生的 map ,但是这个怎么样:

    Object.keys(myObject).map(function(key, index) {
       myObject[key] *= 2;
    });
    
    console.log(myObject);
    
    // => { 'a': 2, 'b': 4, 'c': 6 }
    

    但您可以使用 for ... in 轻松迭代对象:

    for(var key in myObject) {
        if(myObject.hasOwnProperty(key)) {
            myObject[key] *= 2;
        }
    }
    

    Update

    很多人都提到前面的方法不返回一个新对象,而是操作对象本身 . 就此而言,我想添加另一个返回新对象的解决方案,并保留原始对象:

    // 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': 9 }
    
    console.log(myObject);
    // => { 'a': 1, 'b': 2, 'c': 3 }
    

    Array.prototype.reduce通过稍微将先前值与当前值合并,将数组减少为单个值 . 该链由空对象 {} 初始化 . 在每次迭代中,添加 myObject 的新键,其方块为值 .

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

    使用示例代码:

    > 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 的任何现有属性冲突 .

  • 3

    我需要一个允许修改键的版本(基于@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"}
    }
    

    编辑:稍微更改以传入起始对象{} . 允许它为[](如果键是整数)

  • 7

    为了更准确地回应OP所要求的内容,OP需要一个对象:

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

    有一个 Map 方法 myObject.map

    类似于Array.prototype.map,它将按如下方式使用:newObject = myObject.map(function(value,label){
    返回值*值;
    });
    // newObject现在是{'a':1,'b':4,'c':9}

    最好的imho(以“接近所谓的" + "无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;
    }
    

    上面的代码避免故意使用任何语言功能,仅适用于最新的ECMAScript版本 . 用上面的代码可以解决问题lke:

    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

    除了一些人不喜欢之外,还可以像这样在原型链中插入解决方案 .

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

    有些东西,在仔细监督的情况下完成不应该产生任何不良影响,也不会影响其他对象的方法(即Array的 map ) .

  • 2
    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>
    

    使用新的_.transform() to transforms object .

相关问题