【问题标题】:How to generate an key-values inversed object in JavaScript?如何在 JavaScript 中生成键值反转对象?
【发布时间】:2015-07-23 14:20:51
【问题描述】:

更新

我已经调整/更正了示例对象,因为它们之前包含错误。

我有一个如下所示的映射对象:

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

它允许映射一种方式,即AtoB["love"] 产生"amore"。我可以手动添加一个逆,即

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

无论如何,这两个对象同步很麻烦,我想在 Javascript 中以编程方式创建 BtoA。是否有某种 function xyz() 产生 var BtoA = xyz(AtoB);

上面的例子可以扩展到包含一个问题(例如,如果我有太多"beer"

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

因为这不是一对一的映射。用业余数学术语来说它不是一个可逆函数?

为了让事情变得更加复杂,我有一个灾难的秘诀。

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

反转这个 nested javascript 对象,对于这样的xyz() 函数似乎更具挑战性。无论如何,也许有这样一个xyz() 函数,那么我很乐意接受这个作为这个问题的答案

【问题讨论】:

  • 你尝试过什么吗?为什么要反转关键项——将如何使用?
  • 如果值包含数组怎么办?
  • 最后一个例子的逆版本会是什么样子?对于第一个示例,只需 for 超过 Object.keys( AtoB ) 就足够了。
  • @Sirko 根据以前的值是新键的想法,我认为它看起来像var makeStuffInverted = { "food":"agriculture", "beer": {"hops":{...},"water":{...},"malt":{...}}} 。子项仍然是子项。但是主键被映射到新值和以前的值到主键(即第一级)
  • @humanityANDpeace 看看给定的答案。看看能不能解决你的问题。

标签: javascript key inverted-index


【解决方案1】:

非常简单。以下是逆键、值的代码。

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

要将结果放入其他对象,请将其初始化为空并赋值。喜欢

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

还有,JSON:

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

只有三个属性,因为 JSON 是一种字典数据结构:新键将替换旧键。所以上面的 JSON 实际上会是这样的:

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

所以,反转上面给定的 JSON (AtoB) 将导致只反转三个属性,最终结果将是:

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

【讨论】:

  • 谢谢!您对 AtoB 结构的 只有三个值 是正确的,这是我的错误。我实际上是想提供一个具有多个相等值的示例。我更新了我的问题,以更好地反映这一点。正如之前的“更多啤酒”示例实际上只是一个“只有一种啤酒”示例,这不是故意的。也许你可以适应更正后的问题?
  • 非常感谢您的努力和建议的功能。可悲的是,我的用例是嵌套的原始对象。它有点像二维字典。因此,我使用了此处讨论的输入,并为自己创建了一个更复杂的函数,以涵盖更一般的情况。但我认为您的答案最适合简单的情况。
  • @humanityANDpeace ,使用对象作为键不是一个好主意。因此,在我的函数中,如果任何键具有对象值,那么它将反转该对象内的元素(使用递归调用):它不会将对象映射到值。您提供的最新输入将输出为{ "hops":{"water":{"beer":"malt"},"malt":{"beer":"water"}},"water":{"hops":{ "beer":"malt"}, "malt":{"beer":"hops" }},"malt":{"water":{ "beer":"hops"}, "hops":{ "beer":"water" } }, "food":"agriculture"}。 (您可以使用 json 格式化程序以更好的方式查看输出)。
  • @humanityANDpeace 在var AtoB = { "amore" : "love", "alimenti": "food", "Bier" : "beer" "cerveza" : "beer", "pivo" : "beer", "birra" : "beer", "cerveja" : "beer" }; 的情况下,它将产生答案中定义的三个属性,即用新的键替换相同的键。
【解决方案2】:

如果目的/目标是简单的反转(即没有嵌套对象结构;没有多个值),answer from Muhammad imran 是有效的。

显然这是要达到的最佳结果,如果不创建进一步的技巧,以涵盖对象中的键-值关系是:

  • 键是唯一的
  • 值可以是多个

看看上面的啤酒例子,有点遗憾的是信息在反演中丢失了。因此,这个答案应该补充和丰富并提供一种可以存储信息的方式。实现它的方法是在生成的反转对象中使用 Javascript 数组,以允许 存储潜在的模棱两可的新值。例如。

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

允许将 (de,es,pl/cz,it,pt)"beer" 翻译成英文最好存储 这个信息在倒太

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

一个信息丢失较少的版本,原始值“beer”的多重性通过联合下的值多重性来体现,现在倒置键“beer”。

为了实现这一点,我做了一个增强的反相函数

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



}

事实上,由于在简单和嵌套对象的许多地方都可以找到相等的值,因此结果将是一个对象,其中每个值都是一个数组,原因有两个:

  1. 多个原始对象的键可以具有相同的值,因此(反转后)可以存在多个值。一个数组可以存储所有这些多个新值,因此可以存储所有信息。

  2. 在嵌套对象中,唯一性使得每个属性要么是直接值,要么是子对象,在键的反转对象中,我们不仅可以找到多个值,还可以在同一位置找到多个值也是进一步嵌套的对象。 (出于这个原因,幸运的是,作为对象的 Javascript 数组除了其条目之外还允许附加更多属性,因此可以同时用作多个值的存储和嵌套中的子键结构。这种倒置对象结构中的数组的双重用途,不幸的是,很难用 JSON 表示法显示,如 JSON notation does not allow for Arrays with Object Attributes)

【讨论】:

    猜你喜欢
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 2019-12-21
    • 1970-01-01
    • 2022-08-18
    相关资源
    最近更新 更多