map function for objects (instead of arrays)

前端 未结 30 2404
无人及你
无人及你 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: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.

提交回复
热议问题