0

update

I have adjusted/corrected the example objects, because they contained an error before.

I have an mapping object that looks like this:

var AtoB = {
  "amore"   : "love",
  "alimenti": "food",
  "Bier"    : "beer"
};

which allows to map one way i.e. AtoB["love"] yields "amore". I could add an inverse to it manualy i.e.

var BtoA = {
  "love": "amore",
  "food": "alimenti",
  "beer": "Bier"
};

Anyway it troublesome the two objects in sync and I would like to create the BtoA programmatically in Javascript. Is there some sort of function xyz() which yields var BtoA = xyz(AtoB);?

The example above can be extended to include a problem (e.g. if I have too much "beer")

var AtoB = {
  "amore"   : "love",
  "alimenti": "food",
  "Bier"    : "beer"
  "cerveza" : "beer",
  "pivo"    : "beer",
  "birra"   : "beer",
  "cerveja" : "beer"
};

as this is not a 1-to-1 mapping. In amateuer math terms It is not an inversible function?

To make things even more complicated I have a recipe for desaster.

var makeStuff = {
  "agriculture": "food",
  "hops" : {
    "water": {
      "malt": "beer"},
    "malt": {
      "water": "beer"}},
  "water" : {
    "hops": {
      "malt": "beer"},
    "malt": {
      "hops": "beer"}},
  "malt" : {
    "water": {
      "hops": "beer"},
    "hops": {
      "water": "beer"}}
  };

inversing this nested javascript object, seems even more challanging for such an xyz() function. Anyway maybe there is such an xyz() function, then I would be glad to accept this as an answer to this question

7
  • have you attempted anything? Why do you want to reverse key items-how will it be used? Commented Jul 23, 2015 at 14:22
  • 1
    what if value contains an array? Commented Jul 23, 2015 at 14:22
  • What would the inverse version of the last example look like? For the first example a mere for over Object.keys( AtoB ) would suffice. Commented Jul 23, 2015 at 14:25
  • @Sirko I think it would look like var makeStuffInverted = { "food":"agriculture", "beer": {"hops":{...},"water":{...},"malt":{...}}} according to the idea that the former values are the new keys. subkeys remain subkeys. but primary keys get mapped to new values and former values to primary (i.e. first level) keys Commented Jul 23, 2015 at 14:35
  • 1
    @humanityANDpeace look at given answer. see if it solves your problem. Commented Jul 23, 2015 at 15:49

2 Answers 2

1

Very simple. Following is the code to inverse key, value.

var inverse= (function inv(param){

for(var attr in param) {
    if(param.hasOwnProperty(attr)) {
        if(typeof param[attr]==="string") {
            param[param[attr]] = attr;
            delete param[attr];                
        } else if (Object.prototype.toString.call(param[attr]) === "[object Object]") {
            param[attr] = inv(param[attr]);
        }
    }
}
return param;
});

To get the result into other object, initialize it with empty and assign it. Like

var BtoA = {};
BtoA = inverse(AtoB);

And, The JSON:

var AtoB = {
  "love": "amore",
  "food": "alimenti",
  "beer": "Bier",
  "beer": "cerveza",
  "beer": "pivo",
  "beer": "birra",
  "beer": "cerveja",
};

has only three attributes because JSON is a dictionary data structure: new key will replace the old one. So the above JSON will actually be like:

{love: "amore", food: "alimenti", beer: "cerveja"}

So, inverting the above given JSON (AtoB) will result in the inversion of only three properties, and final result will be:

{amore: "love", alimenti: "food", cerveja: "beer"}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you! You are right with the only three value of the AtoB struct, and this was a mistake of mine. I actually meant to provide an example with multiple equal values. I updated my question, to reflect that better. As it was before the "more beer" example was actually simply a "only one beer" example, which was not intented. Maybe you can adjust to the corrected question?
I really appreciate your effort and suggested function. Sadly my use-case is an original object that is nested. It is something like a two dimensional dictionary. Therefore I used the input from the discussions here and created myself a more complex function, to cover the more general case. Yet I think your answer is best for the simple cases.
@humanityANDpeace , using object as a key is not a good idea. So, in my function, if any key has an object value then it will inverse elements inside that object(using recursive call): it'll not map an object to value. the latest input you'be provided will output as { "hops":{"water":{"beer":"malt"},"malt":{"beer":"water"}},"water":{"hops":{ "beer":"malt"}, "malt":{"beer":"hops" }},"malt":{"water":{ "beer":"hops"}, "hops":{ "beer":"water" } }, "food":"agriculture"}. (you can use json formatter to see the output in better way).
@humanityANDpeace And in case of var AtoB = { "amore" : "love", "alimenti": "food", "Bier" : "beer" "cerveza" : "beer", "pivo" : "beer", "birra" : "beer", "cerveja" : "beer" }; it will produce three properties as defined in the answer that same keys are replaced with the new one.
0

The answer from Muhammad imran is effective if the purpose/target is a simple inversion (i.e. no nested object structure; no multiple values).

Obviously that is the best result to be achieved, if no further artifice is created, to cover the fact that the key->value relation in objects are:

  • keys are unique,
  • values can be muliple.

Looking at the beer example above it is somewhat regretible that the information is lost in the inversion. Therefore this answer should supplement and enrich and provide a way in which the information can be stored. The way to achieve it is using Javascript Arrays within the resulting inverted object, to allow to store the potentially ambigious new values. as for example.

var BeerAtoB = {
  "amore"   : "love",
  "alimenti": "food",
  "Bier"    : "beer",
  "cerveza" : "beer",
  "pivo"    : "beer",
  "birra"   : "beer",
  "cerveja" : "beer"
};

allowing to translate (de,es,pl/cz,it,pt)"beer" to English would best store this information in the inverted too

var BeerBtoA = {
  "love" : "amore",
  "food" : "alimenti",
  "beer" : [ "Bier" ,
              "cerveza", 
              "pivo",
              "birra",
              "cerveja"
           ]
};

a version in which less information get lost and the multipleness of the original value "beer" is reflected by multipleness of values under the joint, inverted key "beer" now.

To accomplish this I made an enhanced inverting function

function invertObject(obj) 
{
    var invertedObject = {};
    // make a stack and prime it with the obj
    var stack = [];
    stack.push({"way":[],"obj":obj});

    // while stuff on the stack
    while (stack.length) 
    {
        var way= stack[0].way;
        var obj= stack[0].obj;
        for (var prop in obj) 
        {
            if (typeof obj[prop] === 'object') 
            {
                // attributes, which are themselves objects are added to the stack,
                // with their way information.
                stack.push({"way":way.concat(prop),"obj":obj[prop]});
            } 
            else 
            {
                // always start with adding things to the invertedObject,
                var curobj = invertedObject;
                var value = newKey = obj[prop];
                var curpath = way.concat(prop).concat(obj[prop]);

                // for all but the last two path elements the loop below
                // will create the inverted path, starting with the value (obj[prop])
                // as key, Since values need not be unique (as keys), create each 
                // such new key-property as an Array, not to loose inverted pathes.
                while(curpath.length>2)
                {
                    var pathpart = curpath.pop();
                    if(!curobj.hasOwnProperty(pathpart))
                    {
                       curobj[pathpart]=[];
                    }
                    curobj=curobj[pathpart];
                }

                // the last two curpath Array members represent the last key and the 
                // new to be added value. 
                var preLastPart = curpath.pop();
                var lastPart = curpath.pop();
                // Again the artifice of an Array is used since
                // the inverted keys are not unique, hence cases in which  
                // 1 key has (>1) values.
                if(!curobj.hasOwnProperty(preLastPart))
                {
                    curobj[preLastPart]=[];
                }
                curobj[preLastPart].push(lastPart);
            }
        }
        stack.shift();
    }
    return invertedObject;    function invertObject(obj) 
{
    var invertedObject = {};
    // make a stack and prime it with the obj
    var stack = [];
    stack.push({"way":[],"obj":obj});

    // while stuff on the stack
    while (stack.length) 
    {
        var way= stack[0].way;
        var obj= stack[0].obj;
        for (var prop in obj) 
        {
            if (typeof obj[prop] === 'object') 
            {
                // attributes, which are themselves objects are added to the stack,
                // with their way information.
                stack.push({"way":way.concat(prop),"obj":obj[prop]});
            } 
            else 
            {
                // always start with adding things to the invertedObject,
                var curobj = invertedObject;
                var value = newKey = obj[prop];
                var curpath = way.concat(prop).concat(obj[prop]);

                // for all but the last two path elements the loop below
                // will create the inverted path, starting with the value (obj[prop])
                // as key, Since values need not be unique (as keys), create each 
                // such new key-property as an Array, not to loose inverted pathes.
                while(curpath.length>2)
                {
                    var pathpart = curpath.pop();
                    if(!curobj.hasOwnProperty(pathpart))
                    {
                       curobj[pathpart]=[];
                    }
                    curobj=curobj[pathpart];
                }

                // the last two curpath Array members represent the last key and the 
                // new to be added value. 
                var preLastPart = curpath.pop();
                var lastPart = curpath.pop();
                // Again the artifice of an Array is used since
                // the inverted keys are not unique, hence cases in which  
                // 1 key has (>1) values.
                if(!curobj.hasOwnProperty(preLastPart))
                {
                    curobj[preLastPart]=[];
                }
                curobj[preLastPart].push(lastPart);
            }
        }
        stack.shift();
    }
    return invertedObject;
}



}

Indeed since equal values can be found in many places of simple and nested object, the result will be an object in which each value will be an Array for two reasons:

  1. Several original object's keys, can have the same value, and therefore (after inverting) more than one value can exists. An Array can store all those multiple new values, hence all information.

  2. While in an nested object, the uniqueness makes every property either a direct value or a subobject, in the inverted object at a key, we can find not only muliple values, but also that at the very same place there are also further nested objects. (For this reason it is lucky that an Javascript Array, as being an Object, does besides its entry allow also for further properties to be attached to it and hence can serve simultaneously as a storage for the multiple values and as a subkey in the nested structure. Such a double purpose of Arrays in the inverted object structure, is unforunatelly hard to show in JSON notation, as the JSON notation does not allow for Arrays with Object Attributes)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.