Object key-value map reversal












11














I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.



For example,



var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};


becomes



var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}


and also a function to bring back reversed map to a non-reversed map.



Here's a jsbin to show that my solution is working.



In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.



Just for sake of clarity, _.forOwn iterates over object's own properties, and _.forEach iterates over each element of an array.



var identity = function(x) {return x;};
var reverseMapFromMap = function(map, callback) {
callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
var reversedMap = {};
_.forOwn(map, function(value, key) {
key = callback(key);
if (_.isUndefined(reversedMap[value]))
reversedMap[value] = [key];
else
reversedMap[value].push(key);
});
return reversedMap;
};

var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || identity;
var map = {};
_.forOwn(reverseMap, function(revMembers, revKey) {
_.forEach(revMembers, function(revMember) {
map[revMember] = callback(revKey);
});
});
return map;
};


I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.










share|improve this question





























    11














    I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.



    For example,



    var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};


    becomes



    var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}


    and also a function to bring back reversed map to a non-reversed map.



    Here's a jsbin to show that my solution is working.



    In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.



    Just for sake of clarity, _.forOwn iterates over object's own properties, and _.forEach iterates over each element of an array.



    var identity = function(x) {return x;};
    var reverseMapFromMap = function(map, callback) {
    callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
    var reversedMap = {};
    _.forOwn(map, function(value, key) {
    key = callback(key);
    if (_.isUndefined(reversedMap[value]))
    reversedMap[value] = [key];
    else
    reversedMap[value].push(key);
    });
    return reversedMap;
    };

    var mapFromReverseMap = function(reverseMap, callback) {
    callback = callback || identity;
    var map = {};
    _.forOwn(reverseMap, function(revMembers, revKey) {
    _.forEach(revMembers, function(revMember) {
    map[revMember] = callback(revKey);
    });
    });
    return map;
    };


    I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.










    share|improve this question



























      11












      11








      11


      1





      I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.



      For example,



      var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};


      becomes



      var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}


      and also a function to bring back reversed map to a non-reversed map.



      Here's a jsbin to show that my solution is working.



      In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.



      Just for sake of clarity, _.forOwn iterates over object's own properties, and _.forEach iterates over each element of an array.



      var identity = function(x) {return x;};
      var reverseMapFromMap = function(map, callback) {
      callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
      var reversedMap = {};
      _.forOwn(map, function(value, key) {
      key = callback(key);
      if (_.isUndefined(reversedMap[value]))
      reversedMap[value] = [key];
      else
      reversedMap[value].push(key);
      });
      return reversedMap;
      };

      var mapFromReverseMap = function(reverseMap, callback) {
      callback = callback || identity;
      var map = {};
      _.forOwn(reverseMap, function(revMembers, revKey) {
      _.forEach(revMembers, function(revMember) {
      map[revMember] = callback(revKey);
      });
      });
      return map;
      };


      I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.










      share|improve this question















      I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.



      For example,



      var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};


      becomes



      var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}


      and also a function to bring back reversed map to a non-reversed map.



      Here's a jsbin to show that my solution is working.



      In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.



      Just for sake of clarity, _.forOwn iterates over object's own properties, and _.forEach iterates over each element of an array.



      var identity = function(x) {return x;};
      var reverseMapFromMap = function(map, callback) {
      callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
      var reversedMap = {};
      _.forOwn(map, function(value, key) {
      key = callback(key);
      if (_.isUndefined(reversedMap[value]))
      reversedMap[value] = [key];
      else
      reversedMap[value].push(key);
      });
      return reversedMap;
      };

      var mapFromReverseMap = function(reverseMap, callback) {
      callback = callback || identity;
      var map = {};
      _.forOwn(reverseMap, function(revMembers, revKey) {
      _.forEach(revMembers, function(revMember) {
      map[revMember] = callback(revKey);
      });
      });
      return map;
      };


      I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.







      javascript hash-map






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Sep 29 '14 at 2:24









      Jamal

      30.3k11116226




      30.3k11116226










      asked Aug 31 '14 at 16:29









      Peeyush KushwahaPeeyush Kushwaha

      158119




      158119






















          3 Answers
          3






          active

          oldest

          votes


















          5














          Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.



          One thing I noticed is that you've skipped braces on the if...else in reverseFromMap. My advice is to always use braces, even for one-line expressions.



          However, you could also do



          reversedMap[value] || (reversedMap[value] = );
          reversedMap[value].push(key);


          You can collapse that into 1 line, although it's a little too terse if you ask me



          (reversedMap[value] || (reversedMap[value] = )).push(key);
          // or
          (reversedMap[value] = reversedMap[value] || ).push(key);


          Or just reduce it to a ternary



          reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];


          The _.isUndefined check isn't really required, since reversedMap[key] is either a non-empty array, or it's false'y.



          Other small stuff:




          • I'd prefer the name iterator instead of callback, but lo-dash uses callback in similar situations, so sticking with that convention is fine.



          • You don't necessarily need the identity function, as you could also do



            x = callback ? callback(x) : x;


            and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.



          • If you do need an identity function, lo-dash has one built-in (_.identity). Saves you having to type it out.



          • Lastly, if you want to be stringent, you might want to check whether callback is a function, instead of just doing "default if false'y". E.g.



            callback = _.isFunction(callback) ? callback : _.identity;
            // or just plain JS
            callback = typeof callback === 'function' ? callback : _.identity;


            Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty) callback argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.




          As for alternatives to the overall approach, I'd use _.transform (which is basically the same as reduce/foldl but aimed at objects)



          var reverseMapFromMap = function(map, callback) {
          callback = callback || _.identity;
          return _.transform(map, function (memo, value, key) {
          key = callback(key);
          memo[value] || (memo[value] = );
          memo[value].push(key);
          }, {});
          }


          And again for mapFromReverseMap



          var mapFromReverseMap = function(reverseMap, callback) {
          callback = callback || _.identity;
          return _.transform(reverseMap, function (memo, keys, value) {
          _.forEach(keys, function (key) {
          memo[callback(key)] = value;
          });
          }, {});
          };





          share|improve this answer



















          • 2




            Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
            – janos
            Aug 31 '14 at 17:34










          • @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
            – Flambino
            Aug 31 '14 at 17:52










          • Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
            – Peeyush Kushwaha
            Sep 1 '14 at 10:15



















          4














          I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys and reduce:



          var reverseMapFromMap = function(map, f) {
          return Object.keys(map).reduce(function(acc, k) {
          acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
          return acc
          },{})
          }

          var mapFromReverseMap = function(rMap, f) {
          return Object.keys(rMap).reduce(function(acc, k) {
          rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
          return acc
          },{})
          }


          I renamed identity to id, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.



          The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.



          Other than the missing braces in the if statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f clearly means "function" for example, and x is any value, where k is a "key". After getting used to these conventions, a line like:



          rMap[k].forEach(function(x){acc[x] = (f || id)(k)})


          seems faster to visually parse than:



          rMap[key].forEach(function(value) {
          acc[value] = (callback || identity)(key)
          })


          Eventually your mind removes the function keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:



          rMap[k].forEach(x => acc[x] = (f || id)(k))





          share|improve this answer























          • +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
            – Flambino
            Sep 1 '14 at 8:27










          • On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
            – Peeyush Kushwaha
            Sep 1 '14 at 9:54










          • Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
            – Peeyush Kushwaha
            Sep 1 '14 at 10:16



















          0














          a million ways o solve the same problem of a reverse map...



          mine is like this...
          simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.



          messages={messages.slice(0).reverse().map(
          message => ({ <p>{message}</p> )}
          )}




          share








          New contributor




          KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f61632%2fobject-key-value-map-reversal%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            3 Answers
            3






            active

            oldest

            votes








            3 Answers
            3






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            5














            Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.



            One thing I noticed is that you've skipped braces on the if...else in reverseFromMap. My advice is to always use braces, even for one-line expressions.



            However, you could also do



            reversedMap[value] || (reversedMap[value] = );
            reversedMap[value].push(key);


            You can collapse that into 1 line, although it's a little too terse if you ask me



            (reversedMap[value] || (reversedMap[value] = )).push(key);
            // or
            (reversedMap[value] = reversedMap[value] || ).push(key);


            Or just reduce it to a ternary



            reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];


            The _.isUndefined check isn't really required, since reversedMap[key] is either a non-empty array, or it's false'y.



            Other small stuff:




            • I'd prefer the name iterator instead of callback, but lo-dash uses callback in similar situations, so sticking with that convention is fine.



            • You don't necessarily need the identity function, as you could also do



              x = callback ? callback(x) : x;


              and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.



            • If you do need an identity function, lo-dash has one built-in (_.identity). Saves you having to type it out.



            • Lastly, if you want to be stringent, you might want to check whether callback is a function, instead of just doing "default if false'y". E.g.



              callback = _.isFunction(callback) ? callback : _.identity;
              // or just plain JS
              callback = typeof callback === 'function' ? callback : _.identity;


              Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty) callback argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.




            As for alternatives to the overall approach, I'd use _.transform (which is basically the same as reduce/foldl but aimed at objects)



            var reverseMapFromMap = function(map, callback) {
            callback = callback || _.identity;
            return _.transform(map, function (memo, value, key) {
            key = callback(key);
            memo[value] || (memo[value] = );
            memo[value].push(key);
            }, {});
            }


            And again for mapFromReverseMap



            var mapFromReverseMap = function(reverseMap, callback) {
            callback = callback || _.identity;
            return _.transform(reverseMap, function (memo, keys, value) {
            _.forEach(keys, function (key) {
            memo[callback(key)] = value;
            });
            }, {});
            };





            share|improve this answer



















            • 2




              Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
              – janos
              Aug 31 '14 at 17:34










            • @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
              – Flambino
              Aug 31 '14 at 17:52










            • Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:15
















            5














            Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.



            One thing I noticed is that you've skipped braces on the if...else in reverseFromMap. My advice is to always use braces, even for one-line expressions.



            However, you could also do



            reversedMap[value] || (reversedMap[value] = );
            reversedMap[value].push(key);


            You can collapse that into 1 line, although it's a little too terse if you ask me



            (reversedMap[value] || (reversedMap[value] = )).push(key);
            // or
            (reversedMap[value] = reversedMap[value] || ).push(key);


            Or just reduce it to a ternary



            reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];


            The _.isUndefined check isn't really required, since reversedMap[key] is either a non-empty array, or it's false'y.



            Other small stuff:




            • I'd prefer the name iterator instead of callback, but lo-dash uses callback in similar situations, so sticking with that convention is fine.



            • You don't necessarily need the identity function, as you could also do



              x = callback ? callback(x) : x;


              and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.



            • If you do need an identity function, lo-dash has one built-in (_.identity). Saves you having to type it out.



            • Lastly, if you want to be stringent, you might want to check whether callback is a function, instead of just doing "default if false'y". E.g.



              callback = _.isFunction(callback) ? callback : _.identity;
              // or just plain JS
              callback = typeof callback === 'function' ? callback : _.identity;


              Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty) callback argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.




            As for alternatives to the overall approach, I'd use _.transform (which is basically the same as reduce/foldl but aimed at objects)



            var reverseMapFromMap = function(map, callback) {
            callback = callback || _.identity;
            return _.transform(map, function (memo, value, key) {
            key = callback(key);
            memo[value] || (memo[value] = );
            memo[value].push(key);
            }, {});
            }


            And again for mapFromReverseMap



            var mapFromReverseMap = function(reverseMap, callback) {
            callback = callback || _.identity;
            return _.transform(reverseMap, function (memo, keys, value) {
            _.forEach(keys, function (key) {
            memo[callback(key)] = value;
            });
            }, {});
            };





            share|improve this answer



















            • 2




              Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
              – janos
              Aug 31 '14 at 17:34










            • @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
              – Flambino
              Aug 31 '14 at 17:52










            • Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:15














            5












            5








            5






            Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.



            One thing I noticed is that you've skipped braces on the if...else in reverseFromMap. My advice is to always use braces, even for one-line expressions.



            However, you could also do



            reversedMap[value] || (reversedMap[value] = );
            reversedMap[value].push(key);


            You can collapse that into 1 line, although it's a little too terse if you ask me



            (reversedMap[value] || (reversedMap[value] = )).push(key);
            // or
            (reversedMap[value] = reversedMap[value] || ).push(key);


            Or just reduce it to a ternary



            reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];


            The _.isUndefined check isn't really required, since reversedMap[key] is either a non-empty array, or it's false'y.



            Other small stuff:




            • I'd prefer the name iterator instead of callback, but lo-dash uses callback in similar situations, so sticking with that convention is fine.



            • You don't necessarily need the identity function, as you could also do



              x = callback ? callback(x) : x;


              and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.



            • If you do need an identity function, lo-dash has one built-in (_.identity). Saves you having to type it out.



            • Lastly, if you want to be stringent, you might want to check whether callback is a function, instead of just doing "default if false'y". E.g.



              callback = _.isFunction(callback) ? callback : _.identity;
              // or just plain JS
              callback = typeof callback === 'function' ? callback : _.identity;


              Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty) callback argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.




            As for alternatives to the overall approach, I'd use _.transform (which is basically the same as reduce/foldl but aimed at objects)



            var reverseMapFromMap = function(map, callback) {
            callback = callback || _.identity;
            return _.transform(map, function (memo, value, key) {
            key = callback(key);
            memo[value] || (memo[value] = );
            memo[value].push(key);
            }, {});
            }


            And again for mapFromReverseMap



            var mapFromReverseMap = function(reverseMap, callback) {
            callback = callback || _.identity;
            return _.transform(reverseMap, function (memo, keys, value) {
            _.forEach(keys, function (key) {
            memo[callback(key)] = value;
            });
            }, {});
            };





            share|improve this answer














            Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.



            One thing I noticed is that you've skipped braces on the if...else in reverseFromMap. My advice is to always use braces, even for one-line expressions.



            However, you could also do



            reversedMap[value] || (reversedMap[value] = );
            reversedMap[value].push(key);


            You can collapse that into 1 line, although it's a little too terse if you ask me



            (reversedMap[value] || (reversedMap[value] = )).push(key);
            // or
            (reversedMap[value] = reversedMap[value] || ).push(key);


            Or just reduce it to a ternary



            reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];


            The _.isUndefined check isn't really required, since reversedMap[key] is either a non-empty array, or it's false'y.



            Other small stuff:




            • I'd prefer the name iterator instead of callback, but lo-dash uses callback in similar situations, so sticking with that convention is fine.



            • You don't necessarily need the identity function, as you could also do



              x = callback ? callback(x) : x;


              and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.



            • If you do need an identity function, lo-dash has one built-in (_.identity). Saves you having to type it out.



            • Lastly, if you want to be stringent, you might want to check whether callback is a function, instead of just doing "default if false'y". E.g.



              callback = _.isFunction(callback) ? callback : _.identity;
              // or just plain JS
              callback = typeof callback === 'function' ? callback : _.identity;


              Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty) callback argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.




            As for alternatives to the overall approach, I'd use _.transform (which is basically the same as reduce/foldl but aimed at objects)



            var reverseMapFromMap = function(map, callback) {
            callback = callback || _.identity;
            return _.transform(map, function (memo, value, key) {
            key = callback(key);
            memo[value] || (memo[value] = );
            memo[value].push(key);
            }, {});
            }


            And again for mapFromReverseMap



            var mapFromReverseMap = function(reverseMap, callback) {
            callback = callback || _.identity;
            return _.transform(reverseMap, function (memo, keys, value) {
            _.forEach(keys, function (key) {
            memo[callback(key)] = value;
            });
            }, {});
            };






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 31 '14 at 19:57

























            answered Aug 31 '14 at 17:18









            FlambinoFlambino

            31.6k23386




            31.6k23386








            • 2




              Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
              – janos
              Aug 31 '14 at 17:34










            • @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
              – Flambino
              Aug 31 '14 at 17:52










            • Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:15














            • 2




              Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
              – janos
              Aug 31 '14 at 17:34










            • @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
              – Flambino
              Aug 31 '14 at 17:52










            • Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:15








            2




            2




            Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
            – janos
            Aug 31 '14 at 17:34




            Nice, but I think callback = callback || _.identity is better than callback || (callback = _.identity). It's natural that way, no need to mess with it.
            – janos
            Aug 31 '14 at 17:34












            @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
            – Flambino
            Aug 31 '14 at 17:52




            @janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
            – Flambino
            Aug 31 '14 at 17:52












            Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
            – Peeyush Kushwaha
            Sep 1 '14 at 10:15




            Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
            – Peeyush Kushwaha
            Sep 1 '14 at 10:15













            4














            I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys and reduce:



            var reverseMapFromMap = function(map, f) {
            return Object.keys(map).reduce(function(acc, k) {
            acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
            return acc
            },{})
            }

            var mapFromReverseMap = function(rMap, f) {
            return Object.keys(rMap).reduce(function(acc, k) {
            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
            return acc
            },{})
            }


            I renamed identity to id, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.



            The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.



            Other than the missing braces in the if statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f clearly means "function" for example, and x is any value, where k is a "key". After getting used to these conventions, a line like:



            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})


            seems faster to visually parse than:



            rMap[key].forEach(function(value) {
            acc[value] = (callback || identity)(key)
            })


            Eventually your mind removes the function keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:



            rMap[k].forEach(x => acc[x] = (f || id)(k))





            share|improve this answer























            • +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
              – Flambino
              Sep 1 '14 at 8:27










            • On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
              – Peeyush Kushwaha
              Sep 1 '14 at 9:54










            • Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:16
















            4














            I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys and reduce:



            var reverseMapFromMap = function(map, f) {
            return Object.keys(map).reduce(function(acc, k) {
            acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
            return acc
            },{})
            }

            var mapFromReverseMap = function(rMap, f) {
            return Object.keys(rMap).reduce(function(acc, k) {
            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
            return acc
            },{})
            }


            I renamed identity to id, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.



            The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.



            Other than the missing braces in the if statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f clearly means "function" for example, and x is any value, where k is a "key". After getting used to these conventions, a line like:



            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})


            seems faster to visually parse than:



            rMap[key].forEach(function(value) {
            acc[value] = (callback || identity)(key)
            })


            Eventually your mind removes the function keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:



            rMap[k].forEach(x => acc[x] = (f || id)(k))





            share|improve this answer























            • +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
              – Flambino
              Sep 1 '14 at 8:27










            • On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
              – Peeyush Kushwaha
              Sep 1 '14 at 9:54










            • Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:16














            4












            4








            4






            I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys and reduce:



            var reverseMapFromMap = function(map, f) {
            return Object.keys(map).reduce(function(acc, k) {
            acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
            return acc
            },{})
            }

            var mapFromReverseMap = function(rMap, f) {
            return Object.keys(rMap).reduce(function(acc, k) {
            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
            return acc
            },{})
            }


            I renamed identity to id, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.



            The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.



            Other than the missing braces in the if statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f clearly means "function" for example, and x is any value, where k is a "key". After getting used to these conventions, a line like:



            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})


            seems faster to visually parse than:



            rMap[key].forEach(function(value) {
            acc[value] = (callback || identity)(key)
            })


            Eventually your mind removes the function keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:



            rMap[k].forEach(x => acc[x] = (f || id)(k))





            share|improve this answer














            I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys and reduce:



            var reverseMapFromMap = function(map, f) {
            return Object.keys(map).reduce(function(acc, k) {
            acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
            return acc
            },{})
            }

            var mapFromReverseMap = function(rMap, f) {
            return Object.keys(rMap).reduce(function(acc, k) {
            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
            return acc
            },{})
            }


            I renamed identity to id, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.



            The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.



            Other than the missing braces in the if statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f clearly means "function" for example, and x is any value, where k is a "key". After getting used to these conventions, a line like:



            rMap[k].forEach(function(x){acc[x] = (f || id)(k)})


            seems faster to visually parse than:



            rMap[key].forEach(function(value) {
            acc[value] = (callback || identity)(key)
            })


            Eventually your mind removes the function keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:



            rMap[k].forEach(x => acc[x] = (f || id)(k))






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 31 '14 at 23:08

























            answered Aug 31 '14 at 22:38









            elclanrselclanrs

            2,3221016




            2,3221016












            • +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
              – Flambino
              Sep 1 '14 at 8:27










            • On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
              – Peeyush Kushwaha
              Sep 1 '14 at 9:54










            • Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:16


















            • +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
              – Flambino
              Sep 1 '14 at 8:27










            • On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
              – Peeyush Kushwaha
              Sep 1 '14 at 9:54










            • Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
              – Peeyush Kushwaha
              Sep 1 '14 at 10:16
















            +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
            – Flambino
            Sep 1 '14 at 8:27




            +1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
            – Flambino
            Sep 1 '14 at 8:27












            On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
            – Peeyush Kushwaha
            Sep 1 '14 at 9:54




            On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
            – Peeyush Kushwaha
            Sep 1 '14 at 9:54












            Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
            – Peeyush Kushwaha
            Sep 1 '14 at 10:16




            Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
            – Peeyush Kushwaha
            Sep 1 '14 at 10:16











            0














            a million ways o solve the same problem of a reverse map...



            mine is like this...
            simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.



            messages={messages.slice(0).reverse().map(
            message => ({ <p>{message}</p> )}
            )}




            share








            New contributor




            KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.























              0














              a million ways o solve the same problem of a reverse map...



              mine is like this...
              simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.



              messages={messages.slice(0).reverse().map(
              message => ({ <p>{message}</p> )}
              )}




              share








              New contributor




              KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
              Check out our Code of Conduct.





















                0












                0








                0






                a million ways o solve the same problem of a reverse map...



                mine is like this...
                simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.



                messages={messages.slice(0).reverse().map(
                message => ({ <p>{message}</p> )}
                )}




                share








                New contributor




                KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.









                a million ways o solve the same problem of a reverse map...



                mine is like this...
                simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.



                messages={messages.slice(0).reverse().map(
                message => ({ <p>{message}</p> )}
                )}





                share








                New contributor




                KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.








                share


                share






                New contributor




                KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.









                answered 4 mins ago









                KG ConsultsKG Consults

                1




                1




                New contributor




                KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.





                New contributor





                KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






                KG Consults is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f61632%2fobject-key-value-map-reversal%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    404 Error Contact Form 7 ajax form submitting

                    How to know if a Active Directory user can login interactively

                    TypeError: fit_transform() missing 1 required positional argument: 'X'