map function for objects (instead of arrays)

前端 未结 30 1918
无人及你
无人及你 2020-11-22 04:23

I have an object:

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

I am looking for a native method, similar to Array.prototype.map

相关标签:
30条回答
  • 2020-11-22 04:48

    Based on @Amberlamps answer, here's a utility function (as a comment it looked ugly)

    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}
    
    0 讨论(0)
  • 2020-11-22 04:49

    There is no native map to the Object object, but how about this:

    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:

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

    Update

    With new ES6 features, there is a more elegant way to express 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)) 

    0 讨论(0)
  • 2020-11-22 04:49

    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 do perform normal array operations, including the 'map' function

    0 讨论(0)
  • 2020-11-22 04:52

    You could use Object.keys and then forEach over the returned array of keys:

    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.

    0 讨论(0)
  • 2020-11-22 04:54

    No native methods, but lodash#mapValues will do the job brilliantly

    _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
    // → { 'a': 3, 'b': 6, 'c': 9 }
    
    0 讨论(0)
  • 2020-11-22 04:54

    EDIT: The canonical way using newer JavaScript features is -

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

    1. m, the mapping function – gives you a chance to transform the incoming element before…
    2. r, the reducing function – this function combines the accumulator with the result of the mapped element

    Intuitively, mapReduce creates a new reducer we can plug directly into Array.prototype.reduce. But more importantly, we can implement our object functor implementation omap plainly by utilizing the object monoid, Object.assign and {}.

    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].

    We get a glimpse of mapReduce's sequencing potential if we first abstract 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. Of course the new Object.entries makes this look silly, but the exercise is still important to the learner.

    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. If you're interested in how it is derived and other ways it could be useful, please see this answer.

    0 讨论(0)
提交回复
热议问题